From 64bb279f47a0b102b3c70df2f7565df8be394bf4 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 14 May 2018 11:11:45 +0000 Subject: [PATCH 001/417] [maven-release-plugin] prepare for next development iteration --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 37 files changed, 71 insertions(+), 75 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 73b99ad4ec57..2544dd0fea17 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 6c40b4c5a01c..9aa59bb9c1c8 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.25.Final + 4.1.26.Final-SNAPSHOT pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.25.Final + HEAD @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec-dns - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec-haproxy - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec-http - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec-http2 - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec-memcache - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec-mqtt - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec-redis - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec-smtp - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec-socks - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec-stomp - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-codec-xml - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-common - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-dev-tools - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-handler - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-handler-proxy - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-resolver - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-resolver-dns - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-transport - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-transport-rxtx - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-transport-sctp - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-transport-udt - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-example - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-all - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.25.Final + 4.1.26.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.25.Final + 4.1.26.Final-SNAPSHOT osx-x86_64 io.netty netty-transport-native-epoll - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-transport-native-epoll - 4.1.25.Final + 4.1.26.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.25.Final + 4.1.26.Final-SNAPSHOT io.netty netty-transport-native-kqueue - 4.1.25.Final + 4.1.26.Final-SNAPSHOT osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 82a4f3bab5bc..dac037a31ca3 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 752825becc32..3f7cc1630003 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index f1d984ee7a01..85ec6f33b569 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index cc48581aaefe..ab891b5dd0fd 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 611dc5643812..83b75a4667b5 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index b20a2b0267d0..bd8697a242f4 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index ba849b6f4916..0299e20851eb 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 26b0560e8309..d82cdd93f50a 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index fe20902a73c6..103bf429e901 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 7c0fab500544..bf6062f73e4d 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index d1bcbad87f4b..9adc0eee1cda 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index b6751669467e..146f67f7eb19 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index b23e85d0e73f..8757fe31df2d 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-codec diff --git a/common/pom.xml b/common/pom.xml index 6777210e3f63..530365c37092 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 664bc5954c20..f268b9341c18 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.25.Final + 4.1.26.Final-SNAPSHOT Netty/Dev-Tools @@ -50,8 +50,4 @@ - - - netty-4.1.25.Final - diff --git a/example/pom.xml b/example/pom.xml index f72c6e67cb9b..df4c9a222687 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 17af3380955e..5a649bd1f7dd 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index c663ae7e646c..2f8b393c28a1 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index e6f9e6ebc5bb..e03d8dbbea09 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-microbench diff --git a/pom.xml b/pom.xml index ce2d818e56cd..ae2fbd1e8d97 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.25.Final + 4.1.26.Final-SNAPSHOT Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.25.Final + HEAD diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index 1fedda1f0781..a0089ade9b97 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index 35bb9a01bde1..b199bac951c2 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index c4b85b11aabc..55ccc1c34342 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 881354b79fb6..989f3f980e65 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index aede68b2057a..372f34a9c9ca 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index b928544faf78..9bcce3eb3c81 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-testsuite-osgi diff --git a/testsuite/pom.xml b/testsuite/pom.xml index f486249dbad5..c5806cf1b707 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 5f4d06fd7890..bf5d3e60f1f9 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 28448d6ac77e..865bac1c036f 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 8c91eed64070..ef5346759013 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index b3371ecdfe4a..122f1de44f94 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index 62f1f694bb89..a89807a080e5 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 1034923680d7..e0975c1cc407 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 7e0b38c1677e..414575294b9b 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 9ae36a487cf0..2141c8f21216 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.25.Final + 4.1.26.Final-SNAPSHOT netty-transport From 09d9daf1c4656d86dd88b89b7b9e55bdf6913092 Mon Sep 17 00:00:00 2001 From: zekaryu Date: Mon, 14 May 2018 21:44:32 +0800 Subject: [PATCH 002/417] Update the comment of io.netty.buffer.PoolChunk.java (#7934) Motivation: When I read the source code, I found that the comment of PoolChunk is out of date, it may confuses readers with the description about memoryMap. Modifications: update the last passage of the comment of the PoolChunk class. Result: No change to any source code , just update comment. --- buffer/src/main/java/io/netty/buffer/PoolChunk.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunk.java b/buffer/src/main/java/io/netty/buffer/PoolChunk.java index b3ca160223a7..93eda84104bf 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolChunk.java +++ b/buffer/src/main/java/io/netty/buffer/PoolChunk.java @@ -94,11 +94,10 @@ * Note: * ----- * In the implementation for improving cache coherence, - * we store 2 pieces of information (i.e, 2 byte vals) as a short value in memoryMap + * we store 2 pieces of information depth_of_id and x as two byte values in memoryMap and depthMap respectively * - * memoryMap[id]= (depth_of_id, x) - * where as per convention defined above - * the second value (i.e, x) indicates that the first node which is free to be allocated is at depth x (from root) + * memoryMap[id]= depth_of_id is defined above + * depthMap[id]= x indicates that the first node which is free to be allocated is at depth x (from root) */ final class PoolChunk implements PoolChunkMetric { From 30a02b441aa967bf1cf9ae8dc9feb8a8ce5090df Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Tue, 15 May 2018 00:06:32 -0700 Subject: [PATCH 003/417] Avoid implicit allocations in Http2FrameLogger when logging is disabled (#7937) Motivation: Integer autoboxing in this class (and possibly also the varargs arrays) showed non-negligible CPU and garbage contribution when profiling a gRPC service. grpc-java currently hardcodes use of Http2FrameLogger, set at DEBUG level. Modifications: Wrap offending log statements in conditional blocks. Result: Garbage won't be produced by Http2FrameLogger when set to a disabled logging level. --- .../handler/codec/http2/Http2FrameLogger.java | 81 ++++++++++++------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameLogger.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameLogger.java index 8a67e5f87ad0..791e991cb841 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameLogger.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameLogger.java @@ -20,7 +20,6 @@ import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.logging.LogLevel; -import io.netty.util.internal.StringUtil; import io.netty.util.internal.UnstableApi; import io.netty.util.internal.logging.InternalLogLevel; import io.netty.util.internal.logging.InternalLogger; @@ -60,34 +59,48 @@ private Http2FrameLogger(InternalLogLevel level, InternalLogger logger) { this.logger = checkNotNull(logger, "logger"); } + public boolean isEnabled() { + return logger.isEnabled(level); + } + public void logData(Direction direction, ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endStream) { - logger.log(level, "{} {} DATA: streamId={} padding={} endStream={} length={} bytes={}", ctx.channel(), - direction.name(), streamId, padding, endStream, data.readableBytes(), toString(data)); + if (isEnabled()) { + logger.log(level, "{} {} DATA: streamId={} padding={} endStream={} length={} bytes={}", ctx.channel(), + direction.name(), streamId, padding, endStream, data.readableBytes(), toString(data)); + } } public void logHeaders(Direction direction, ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, boolean endStream) { - logger.log(level, "{} {} HEADERS: streamId={} headers={} padding={} endStream={}", ctx.channel(), - direction.name(), streamId, headers, padding, endStream); + if (isEnabled()) { + logger.log(level, "{} {} HEADERS: streamId={} headers={} padding={} endStream={}", ctx.channel(), + direction.name(), streamId, headers, padding, endStream); + } } public void logHeaders(Direction direction, ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) { - logger.log(level, "{} {} HEADERS: streamId={} headers={} streamDependency={} weight={} exclusive={} " + - "padding={} endStream={}", ctx.channel(), - direction.name(), streamId, headers, streamDependency, weight, exclusive, padding, endStream); + if (isEnabled()) { + logger.log(level, "{} {} HEADERS: streamId={} headers={} streamDependency={} weight={} exclusive={} " + + "padding={} endStream={}", ctx.channel(), + direction.name(), streamId, headers, streamDependency, weight, exclusive, padding, endStream); + } } public void logPriority(Direction direction, ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, boolean exclusive) { - logger.log(level, "{} {} PRIORITY: streamId={} streamDependency={} weight={} exclusive={}", ctx.channel(), - direction.name(), streamId, streamDependency, weight, exclusive); + if (isEnabled()) { + logger.log(level, "{} {} PRIORITY: streamId={} streamDependency={} weight={} exclusive={}", ctx.channel(), + direction.name(), streamId, streamDependency, weight, exclusive); + } } public void logRstStream(Direction direction, ChannelHandlerContext ctx, int streamId, long errorCode) { - logger.log(level, "{} {} RST_STREAM: streamId={} errorCode={}", ctx.channel(), - direction.name(), streamId, errorCode); + if (isEnabled()) { + logger.log(level, "{} {} RST_STREAM: streamId={} errorCode={}", ctx.channel(), + direction.name(), streamId, errorCode); + } } public void logSettingsAck(Direction direction, ChannelHandlerContext ctx) { @@ -95,48 +108,58 @@ public void logSettingsAck(Direction direction, ChannelHandlerContext ctx) { } public void logSettings(Direction direction, ChannelHandlerContext ctx, Http2Settings settings) { - logger.log(level, "{} {} SETTINGS: ack=false settings={}", ctx.channel(), direction.name(), settings); + if (isEnabled()) { + logger.log(level, "{} {} SETTINGS: ack=false settings={}", ctx.channel(), direction.name(), settings); + } } public void logPing(Direction direction, ChannelHandlerContext ctx, long data) { - logger.log(level, "{} {} PING: ack=false bytes={}", ctx.channel(), - direction.name(), data); + if (isEnabled()) { + logger.log(level, "{} {} PING: ack=false bytes={}", ctx.channel(), + direction.name(), data); + } } public void logPingAck(Direction direction, ChannelHandlerContext ctx, long data) { - logger.log(level, "{} {} PING: ack=true bytes={}", ctx.channel(), - direction.name(), data); + if (isEnabled()) { + logger.log(level, "{} {} PING: ack=true bytes={}", ctx.channel(), + direction.name(), data); + } } public void logPushPromise(Direction direction, ChannelHandlerContext ctx, int streamId, int promisedStreamId, Http2Headers headers, int padding) { - logger.log(level, "{} {} PUSH_PROMISE: streamId={} promisedStreamId={} headers={} padding={}", ctx.channel(), - direction.name(), streamId, promisedStreamId, headers, padding); + if (isEnabled()) { + logger.log(level, "{} {} PUSH_PROMISE: streamId={} promisedStreamId={} headers={} padding={}", + ctx.channel(), direction.name(), streamId, promisedStreamId, headers, padding); + } } public void logGoAway(Direction direction, ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) { - logger.log(level, "{} {} GO_AWAY: lastStreamId={} errorCode={} length={} bytes={}", ctx.channel(), - direction.name(), lastStreamId, errorCode, debugData.readableBytes(), toString(debugData)); + if (isEnabled()) { + logger.log(level, "{} {} GO_AWAY: lastStreamId={} errorCode={} length={} bytes={}", ctx.channel(), + direction.name(), lastStreamId, errorCode, debugData.readableBytes(), toString(debugData)); + } } public void logWindowsUpdate(Direction direction, ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) { - logger.log(level, "{} {} WINDOW_UPDATE: streamId={} windowSizeIncrement={}", ctx.channel(), - direction.name(), streamId, windowSizeIncrement); + if (isEnabled()) { + logger.log(level, "{} {} WINDOW_UPDATE: streamId={} windowSizeIncrement={}", ctx.channel(), + direction.name(), streamId, windowSizeIncrement); + } } public void logUnknownFrame(Direction direction, ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, ByteBuf data) { - logger.log(level, "{} {} UNKNOWN: frameType={} streamId={} flags={} length={} bytes={}", ctx.channel(), - direction.name(), frameType & 0xFF, streamId, flags.value(), data.readableBytes(), toString(data)); + if (isEnabled()) { + logger.log(level, "{} {} UNKNOWN: frameType={} streamId={} flags={} length={} bytes={}", ctx.channel(), + direction.name(), frameType & 0xFF, streamId, flags.value(), data.readableBytes(), toString(data)); + } } private String toString(ByteBuf buf) { - if (!logger.isEnabled(level)) { - return StringUtil.EMPTY_STRING; - } - if (level == InternalLogLevel.TRACE || buf.readableBytes() <= BUFFER_LENGTH_THRESHOLD) { // Log the entire buffer. return ByteBufUtil.hexDump(buf); From 3d2e231794fea07a183e328061cb1d77fccebe82 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 15 May 2018 10:39:14 +0200 Subject: [PATCH 004/417] Add docker-sync files to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a3c75313a21c..a3f28acc926c 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ hs_err_pid*.log dependency-reduced-pom.xml + +*/.unison.* From 85e2987719fb6e196feb13a21560cc1ea589e24a Mon Sep 17 00:00:00 2001 From: ossdev07 <39188636+ossdev07@users.noreply.github.com> Date: Tue, 15 May 2018 15:08:15 +0530 Subject: [PATCH 005/417] =?UTF-8?q?pom.xml:=20adding=20suuport=20for=20AAR?= =?UTF-8?q?CH64=20architecture=20in=20Netty/Transport/N=E2=80=A6=20(#7933)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Netty/Transport/Native/Epoll project can be build on aarch64 platform as well. Motivation: To provide the support for AARCH64 architecture Modification: Adjusted regex for enforce plugin to also allow AARCH64. Result: Be able to compile on AARCH64 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ae2fbd1e8d97..7dbcefc49190 100644 --- a/pom.xml +++ b/pom.xml @@ -637,10 +637,10 @@ - x86_64 JDK must be used. + x86_64/AARCH64 JDK must be used. os.detected.arch - ^x86_64$ + ^(x86_64|aarch_64)$ From 1d09efeeb2535869cd0eaf3e1b239273e239c817 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 15 May 2018 19:41:45 +0200 Subject: [PATCH 006/417] Correctly copy existing elements when CodecOutputList.add(index, element) is called. (#7939) Motivation: We did not correctly copy elements in some cases when add(index, element) was used. Modifications: - Correctly detect when copy is neede and when not. - Add test case. Result: Fixes https://github.com/netty/netty/issues/7938. --- .../netty/handler/codec/CodecOutputList.java | 2 +- .../handler/codec/CodecOutputListTest.java | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 codec/src/test/java/io/netty/handler/codec/CodecOutputListTest.java diff --git a/codec/src/main/java/io/netty/handler/codec/CodecOutputList.java b/codec/src/main/java/io/netty/handler/codec/CodecOutputList.java index bd707ec5d31d..5cf161fc694c 100644 --- a/codec/src/main/java/io/netty/handler/codec/CodecOutputList.java +++ b/codec/src/main/java/io/netty/handler/codec/CodecOutputList.java @@ -148,7 +148,7 @@ public void add(int index, Object element) { expandArray(); } - if (index != size - 1) { + if (index != size) { System.arraycopy(array, index, array, index + 1, size - index); } diff --git a/codec/src/test/java/io/netty/handler/codec/CodecOutputListTest.java b/codec/src/test/java/io/netty/handler/codec/CodecOutputListTest.java new file mode 100644 index 000000000000..1aa0927999da --- /dev/null +++ b/codec/src/test/java/io/netty/handler/codec/CodecOutputListTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec; + +import org.junit.Test; + + +import static org.junit.Assert.*; + +public class CodecOutputListTest { + + @Test + public void testCodecOutputListAdd() { + CodecOutputList codecOutputList = CodecOutputList.newInstance(); + try { + assertEquals(0, codecOutputList.size()); + assertTrue(codecOutputList.isEmpty()); + + codecOutputList.add(1); + assertEquals(1, codecOutputList.size()); + assertFalse(codecOutputList.isEmpty()); + assertEquals(1, codecOutputList.get(0)); + + codecOutputList.add(0, 0); + assertEquals(2, codecOutputList.size()); + assertFalse(codecOutputList.isEmpty()); + assertEquals(0, codecOutputList.get(0)); + assertEquals(1, codecOutputList.get(1)); + + codecOutputList.add(1, 2); + assertEquals(3, codecOutputList.size()); + assertFalse(codecOutputList.isEmpty()); + assertEquals(0, codecOutputList.get(0)); + assertEquals(2, codecOutputList.get(1)); + assertEquals(1, codecOutputList.get(2)); + } finally { + codecOutputList.recycle(); + } + } +} From ed54ab034dcc444dffbcaaa5675c39abc0cfbff3 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 15 May 2018 19:44:02 +0200 Subject: [PATCH 007/417] Correctly clear the error stack in all cases when using ReferenceCountedOpenSslEngine. (#7941) Motivation: We missed to correctly clear the error stack in one case when using the ReferenceCountedOpenSslEngine. Because of this it was possible to pick up an error on an unrelated operation. Modifications: - Correctly clear the stack - Add verification of empty error stack to tests. Result: Not possible to observe unrelated errors. --- .../netty/handler/ssl/ReferenceCountedOpenSslEngine.java | 9 +++++---- .../java/io/netty/handler/ssl/OpenSslEngineTest.java | 8 ++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index c0eb141a5f33..aa03cf0a2735 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -1093,8 +1093,6 @@ public final SSLEngineResult unwrap( } private SSLEngineResult sslReadErrorResult(int err, int bytesConsumed, int bytesProduced) throws SSLException { - String errStr = SSL.getErrorString(err); - // Check if we have a pending handshakeException and if so see if we need to consume all pending data from the // BIO first or can just shutdown and throw it now. // This is needed so we ensure close_notify etc is correctly send to the remote peer. @@ -1103,11 +1101,14 @@ private SSLEngineResult sslReadErrorResult(int err, int bytesConsumed, int bytes if (handshakeException == null && handshakeState != HandshakeState.FINISHED) { // we seems to have data left that needs to be transfered and so the user needs // call wrap(...). Store the error so we can pick it up later. - handshakeException = new SSLHandshakeException(errStr); + handshakeException = new SSLHandshakeException(SSL.getErrorString(err)); } + // We need to clear all errors so we not pick up anything that was left on the stack on the next + // operation. Note that shutdownWithError(...) will cleanup the stack as well so its only needed here. + SSL.clearError(); return new SSLEngineResult(OK, NEED_WRAP, bytesConsumed, bytesProduced); } - throw shutdownWithError("SSL_read", errStr); + throw shutdownWithError("SSL_read", SSL.getErrorString(err)); } private void closeAll() throws SSLException { diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index 9972b2d5f4b4..f193468d1bc5 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -21,7 +21,9 @@ import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; +import io.netty.internal.tcnative.SSL; import io.netty.util.internal.PlatformDependent; +import org.junit.Assert; import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; @@ -82,6 +84,12 @@ public static void checkOpenSsl() { assumeTrue(OpenSsl.isAvailable()); } + @Override + public void tearDown() throws InterruptedException { + super.tearDown(); + Assert.assertEquals("SSL error stack not correctly consumed", 0, SSL.getLastErrorNumber()); + } + @Override @Test public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth() throws Exception { From 69c644bb9823eace6c7974c497689b41e6c06a4b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 16 May 2018 07:23:55 +0200 Subject: [PATCH 008/417] Correctly detect if protocol is enabled when using netty-tcnative (#7940) Motivation: We sometimes did not correctly detect when a protocol is not enabled when using netty-tcnative as we did not take into account when the option flag is 0 (as for example in BoringSSL for SSLv2). Modifications: - Correctly take an option flag with 0 into account. - Add unit tests. Result: Fixes https://github.com/netty/netty/issues/7935. --- .../java/io/netty/handler/ssl/OpenSsl.java | 18 ++++--- .../io/netty/handler/ssl/SSLEngineTest.java | 50 +++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index 165b5cbc896e..6ad934fbc652 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -217,19 +217,19 @@ public Boolean run() { Set protocols = new LinkedHashSet(6); // Seems like there is no way to explicitly disable SSLv2Hello in openssl so it is always enabled protocols.add(PROTOCOL_SSL_V2_HELLO); - if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV2)) { + if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV2, SSL.SSL_OP_NO_SSLv2)) { protocols.add(PROTOCOL_SSL_V2); } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV3)) { + if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV3, SSL.SSL_OP_NO_SSLv3)) { protocols.add(PROTOCOL_SSL_V3); } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1)) { + if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1, SSL.SSL_OP_NO_TLSv1)) { protocols.add(PROTOCOL_TLS_V1); } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_1)) { + if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_1, SSL.SSL_OP_NO_TLSv1_1)) { protocols.add(PROTOCOL_TLS_V1_1); } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_2)) { + if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_2, SSL.SSL_OP_NO_TLSv1_2)) { protocols.add(PROTOCOL_TLS_V1_2); } @@ -237,7 +237,7 @@ public Boolean run() { SUPPORTS_OCSP = doesSupportOcsp(); if (logger.isDebugEnabled()) { - logger.debug("Supported protocols (OpenSSL): {} ", Arrays.asList(SUPPORTED_PROTOCOLS_SET)); + logger.debug("Supported protocols (OpenSSL): {} ", SUPPORTED_PROTOCOLS_SET); logger.debug("Default cipher suites (OpenSSL): {}", DEFAULT_CIPHERS); } } else { @@ -271,7 +271,11 @@ private static boolean doesSupportOcsp() { } return supportsOcsp; } - private static boolean doesSupportProtocol(int protocol) { + private static boolean doesSupportProtocol(int protocol, int opt) { + if (opt == 0) { + // If the opt is 0 the protocol is not supported. This is for example the case with BoringSSL and SSLv2. + return false; + } long sslCtx = -1; try { sslCtx = SSLContext.make(protocol, SSL.SSL_MODE_COMBINED); diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 7d86fa278e5b..9924a5f1f395 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -44,6 +44,7 @@ import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -62,7 +63,9 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -76,7 +79,11 @@ import javax.net.ssl.SSLSession; import javax.security.cert.X509Certificate; +import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2; +import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2_HELLO; +import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V3; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1; +import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH; @@ -2355,4 +2362,47 @@ public void testWrapDoesNotZeroOutSrc() throws Exception { cert.delete(); } } + + @Test + public void testDisableProtocols() throws Exception { + testDisableProtocols(PROTOCOL_SSL_V2, PROTOCOL_SSL_V2); + testDisableProtocols(PROTOCOL_SSL_V3, PROTOCOL_SSL_V2, PROTOCOL_SSL_V3); + testDisableProtocols(PROTOCOL_TLS_V1, PROTOCOL_SSL_V2, PROTOCOL_SSL_V3, PROTOCOL_TLS_V1); + testDisableProtocols(PROTOCOL_TLS_V1_1, PROTOCOL_SSL_V2, PROTOCOL_SSL_V3, PROTOCOL_TLS_V1, PROTOCOL_TLS_V1_1); + testDisableProtocols(PROTOCOL_TLS_V1_2, PROTOCOL_SSL_V2, + PROTOCOL_SSL_V3, PROTOCOL_TLS_V1, PROTOCOL_TLS_V1_1, PROTOCOL_TLS_V1_2); + } + + private void testDisableProtocols(String protocol, String... disabledProtocols) throws Exception { + SelfSignedCertificate cert = new SelfSignedCertificate(); + + SslContext ctx = SslContextBuilder + .forServer(cert.certificate(), cert.privateKey()) + .sslProvider(sslServerProvider()) + .build(); + SSLEngine server = ctx.newEngine(UnpooledByteBufAllocator.DEFAULT); + + try { + Set supported = new HashSet(Arrays.asList(server.getSupportedProtocols())); + if (supported.contains(protocol)) { + server.setEnabledProtocols(server.getSupportedProtocols()); + Assert.assertEquals(supported, new HashSet(Arrays.asList(server.getSupportedProtocols()))); + + for (String disabled: disabledProtocols) { + supported.remove(disabled); + } + if (supported.contains(SslUtils.PROTOCOL_SSL_V2_HELLO) && supported.size() == 1) { + // It's not allowed to set only PROTOCOL_SSL_V2_HELLO if using JDK SSLEngine. + return; + } + server.setEnabledProtocols(supported.toArray(new String[0])); + Assert.assertEquals(supported, new HashSet(Arrays.asList(server.getEnabledProtocols()))); + server.setEnabledProtocols(server.getSupportedProtocols()); + } + } finally { + cleanupServerSslEngine(server); + cleanupClientSslContext(ctx); + cert.delete(); + } + } } From 932d77b83e33087bfa5e74a14db2730615ab2d93 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 16 May 2018 13:50:37 +0200 Subject: [PATCH 009/417] Verify error stack is empty after each operation when using ReferenceCountedOpenSslEngine. (#7943) Motivation: https://github.com/netty/netty/pull/7941 proved that its easy to not correctly clear the error stack sometimes. We should do carefully test this. Modifications: Add a new SSLEngine wrapper that is used during tests, which verifies that the error stack is empty after each method call. Result: Better testing. --- .../netty/handler/ssl/Java8SslTestUtils.java | 11 + .../ssl/JdkOpenSslEngineInteroptTest.java | 6 + .../netty/handler/ssl/OpenSslEngineTest.java | 67 +-- .../ssl/OpenSslErrorStackAssertSSLEngine.java | 440 ++++++++++++++++++ .../ssl/OpenSslJdkSslEngineInteroptTest.java | 6 + .../io/netty/handler/ssl/SSLEngineTest.java | 80 ++-- 6 files changed, 546 insertions(+), 64 deletions(-) create mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java diff --git a/handler/src/test/java/io/netty/handler/ssl/Java8SslTestUtils.java b/handler/src/test/java/io/netty/handler/ssl/Java8SslTestUtils.java index cc2e6c6ed325..32219ff155a7 100644 --- a/handler/src/test/java/io/netty/handler/ssl/Java8SslTestUtils.java +++ b/handler/src/test/java/io/netty/handler/ssl/Java8SslTestUtils.java @@ -20,6 +20,7 @@ import javax.net.ssl.SNIMatcher; import javax.net.ssl.SNIServerName; +import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import java.security.Provider; import java.util.Collections; @@ -41,4 +42,14 @@ public boolean matches(SNIServerName sniServerName) { static Provider conscryptProvider() { return new OpenSSLProvider(); } + + /** + * Wraps the given {@link SSLEngine} to add extra tests while executing methods if possible / needed. + */ + static SSLEngine wrapSSLEngineForTesting(SSLEngine engine) { + if (engine instanceof ReferenceCountedOpenSslEngine) { + return new OpenSslErrorStackAssertSSLEngine((ReferenceCountedOpenSslEngine) engine); + } + return engine; + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java index d696d6b2e7a5..0eed0b3087d5 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java @@ -20,6 +20,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import javax.net.ssl.SSLEngine; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -105,4 +106,9 @@ protected boolean mySetupMutualAuthServerIsValidClientException(Throwable cause) // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. return super.mySetupMutualAuthServerIsValidClientException(cause) || causedBySSLException(cause); } + + @Override + protected SSLEngine wrapEngine(SSLEngine engine) { + return Java8SslTestUtils.wrapSSLEngineForTesting(engine); + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index f193468d1bc5..9949dd918993 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -87,7 +87,7 @@ public static void checkOpenSsl() { @Override public void tearDown() throws InterruptedException { super.tearDown(); - Assert.assertEquals("SSL error stack not correctly consumed", 0, SSL.getLastErrorNumber()); + assertEquals("SSL error stack not correctly consumed", 0, SSL.getLastErrorNumber()); } @Override @@ -215,8 +215,8 @@ public void testWrapBuffersNoWritePendingError() throws Exception { SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { - clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); handshake(clientEngine, serverEngine); ByteBuffer src = allocateBuffer(1024 * 10); @@ -249,8 +249,8 @@ public void testOnlySmallBufferNeededForWrap() throws Exception { SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { - clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); handshake(clientEngine, serverEngine); // Allocate a buffer which is small enough and set the limit to the capacity to mark its whole content @@ -259,9 +259,9 @@ public void testOnlySmallBufferNeededForWrap() throws Exception { ByteBuffer src = allocateBuffer(srcLen); ByteBuffer dstTooSmall = allocateBuffer( - src.capacity() + ((ReferenceCountedOpenSslEngine) clientEngine).maxWrapOverhead() - 1); + src.capacity() + unwrapEngine(clientEngine).maxWrapOverhead() - 1); ByteBuffer dst = allocateBuffer( - src.capacity() + ((ReferenceCountedOpenSslEngine) clientEngine).maxWrapOverhead()); + src.capacity() + unwrapEngine(clientEngine).maxWrapOverhead()); // Check that we fail to wrap if the dst buffers capacity is not at least // src.capacity() + ReferenceCountedOpenSslEngine.MAX_TLS_RECORD_OVERHEAD_LENGTH @@ -300,15 +300,15 @@ public void testNeededDstCapacityIsCorrectlyCalculated() throws Exception { SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { - clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); handshake(clientEngine, serverEngine); ByteBuffer src = allocateBuffer(1024); ByteBuffer src2 = src.duplicate(); ByteBuffer dst = allocateBuffer(src.capacity() - + ((ReferenceCountedOpenSslEngine) clientEngine).maxWrapOverhead()); + + unwrapEngine(clientEngine).maxWrapOverhead()); SSLEngineResult result = clientEngine.wrap(new ByteBuffer[] { src, src2 }, dst); assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); @@ -336,8 +336,8 @@ public void testSrcsLenOverFlowCorrectlyHandled() throws Exception { SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { - clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); handshake(clientEngine, serverEngine); ByteBuffer src = allocateBuffer(1024); @@ -353,7 +353,7 @@ public void testSrcsLenOverFlowCorrectlyHandled() throws Exception { ByteBuffer[] srcs = srcList.toArray(new ByteBuffer[srcList.size()]); ByteBuffer dst = allocateBuffer( - ((ReferenceCountedOpenSslEngine) clientEngine).maxEncryptedPacketLength() - 1); + unwrapEngine(clientEngine).maxEncryptedPacketLength() - 1); SSLEngineResult result = clientEngine.wrap(srcs, dst); assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); @@ -596,13 +596,13 @@ public void testMultipleRecordsInOneBufferWithNonZeroPositionJDKCompatabilityMod .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine(); + SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine(); + SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); try { // Choose buffer size small enough that we can put multiple buffers into one buffer and pass it into the @@ -673,13 +673,13 @@ public void testInputTooBigAndFillsUpBuffersJDKCompatabilityModeOff() throws Exc .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine(); + SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine(); + SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); try { ByteBuffer plainClient = allocateBuffer(MAX_PLAINTEXT_LENGTH + 100); @@ -757,13 +757,13 @@ public void testPartialPacketUnwrapJDKCompatabilityModeOff() throws Exception { .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine(); + SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine(); + SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); try { ByteBuffer plainClient = allocateBuffer(1024); @@ -832,13 +832,13 @@ public void testBufferUnderFlowAvoidedIfJDKCompatabilityModeOff() throws Excepti .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine(); + SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine(); + SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); try { ByteBuffer plainClient = allocateBuffer(1024); @@ -913,8 +913,8 @@ private void testWrapWithDifferentSizes(String protocol, String cipher) throws E SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { - clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); clientEngine.setEnabledCipherSuites(new String[] { cipher }); clientEngine.setEnabledProtocols(new String[] { protocol }); serverEngine.setEnabledCipherSuites(new String[] { cipher }); @@ -944,7 +944,7 @@ private void testWrapWithDifferentSizes(String protocol, String cipher) throws E private void testWrapDstBigEnough(SSLEngine engine, int srcLen) throws SSLException { ByteBuffer src = allocateBuffer(srcLen); - ByteBuffer dst = allocateBuffer(srcLen + ((ReferenceCountedOpenSslEngine) engine).maxWrapOverhead()); + ByteBuffer dst = allocateBuffer(srcLen + unwrapEngine(engine).maxWrapOverhead()); SSLEngineResult result = engine.wrap(src, dst); assertEquals(SSLEngineResult.Status.OK, result.getStatus()); @@ -966,7 +966,7 @@ public void testSNIMatchersDoesNotThrow() throws Exception { .sslProvider(sslServerProvider()) .build(); - SSLEngine engine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { SSLParameters parameters = new SSLParameters(); Java8SslTestUtils.setSNIMatcher(parameters); @@ -984,7 +984,7 @@ public void testAlgorithmConstraintsThrows() throws Exception { .sslProvider(sslServerProvider()) .build(); - SSLEngine engine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { SSLParameters parameters = new SSLParameters(); parameters.setAlgorithmConstraints(new AlgorithmConstraints() { @@ -1029,4 +1029,19 @@ private static ApplicationProtocolConfig acceptingNegotiator(Protocol protocol, SelectedListenerFailureBehavior.ACCEPT, supportedProtocols); } + + @Override + protected SSLEngine wrapEngine(SSLEngine engine) { + if (PlatformDependent.javaVersion() >= 8) { + return Java8SslTestUtils.wrapSSLEngineForTesting(engine); + } + return engine; + } + + ReferenceCountedOpenSslEngine unwrapEngine(SSLEngine engine) { + if (engine instanceof JdkSslEngine) { + return (ReferenceCountedOpenSslEngine) ((JdkSslEngine) engine).getWrappedEngine(); + } + return (ReferenceCountedOpenSslEngine) engine; + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java new file mode 100644 index 000000000000..8d43612584bf --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java @@ -0,0 +1,440 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.internal.tcnative.SSL; +import io.netty.util.ReferenceCounted; +import io.netty.util.internal.PlatformDependent; +import org.junit.Assert; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSession; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.function.BiFunction; + +/** + * Special {@link SSLEngine} which allows to wrap a {@link ReferenceCountedOpenSslEngine} and verify that that + * Error stack is empty after each method call. + */ +final class OpenSslErrorStackAssertSSLEngine extends JdkSslEngine implements ReferenceCounted { + + OpenSslErrorStackAssertSSLEngine(ReferenceCountedOpenSslEngine engine) { + super(engine); + } + + @Override + public String getPeerHost() { + try { + return getWrappedEngine().getPeerHost(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public int getPeerPort() { + try { + return getWrappedEngine().getPeerPort(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) throws SSLException { + try { + return getWrappedEngine().wrap(src, dst); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public SSLEngineResult wrap(ByteBuffer[] srcs, ByteBuffer dst) throws SSLException { + try { + return getWrappedEngine().wrap(srcs, dst); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public SSLEngineResult wrap(ByteBuffer[] byteBuffers, int i, int i1, ByteBuffer byteBuffer) throws SSLException { + try { + return getWrappedEngine().wrap(byteBuffers, i, i1, byteBuffer); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) throws SSLException { + try { + return getWrappedEngine().unwrap(src, dst); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) throws SSLException { + try { + return getWrappedEngine().unwrap(src, dsts); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers, int i, int i1) throws SSLException { + try { + return getWrappedEngine().unwrap(byteBuffer, byteBuffers, i, i1); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public Runnable getDelegatedTask() { + try { + return getWrappedEngine().getDelegatedTask(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public void closeInbound() throws SSLException { + try { + getWrappedEngine().closeInbound(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public boolean isInboundDone() { + try { + return getWrappedEngine().isInboundDone(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public void closeOutbound() { + try { + getWrappedEngine().closeOutbound(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public boolean isOutboundDone() { + try { + return getWrappedEngine().isOutboundDone(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public String[] getSupportedCipherSuites() { + try { + return getWrappedEngine().getSupportedCipherSuites(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public String[] getEnabledCipherSuites() { + try { + return getWrappedEngine().getEnabledCipherSuites(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public void setEnabledCipherSuites(String[] strings) { + try { + getWrappedEngine().setEnabledCipherSuites(strings); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public String[] getSupportedProtocols() { + try { + return getWrappedEngine().getSupportedProtocols(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public String[] getEnabledProtocols() { + try { + return getWrappedEngine().getEnabledProtocols(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public void setEnabledProtocols(String[] strings) { + try { + getWrappedEngine().setEnabledProtocols(strings); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public SSLSession getSession() { + try { + return getWrappedEngine().getSession(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public SSLSession getHandshakeSession() { + try { + return getWrappedEngine().getHandshakeSession(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public void beginHandshake() throws SSLException { + try { + getWrappedEngine().beginHandshake(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public SSLEngineResult.HandshakeStatus getHandshakeStatus() { + try { + return getWrappedEngine().getHandshakeStatus(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public void setUseClientMode(boolean b) { + try { + getWrappedEngine().setUseClientMode(b); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public boolean getUseClientMode() { + try { + return getWrappedEngine().getUseClientMode(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public void setNeedClientAuth(boolean b) { + try { + getWrappedEngine().setNeedClientAuth(b); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public boolean getNeedClientAuth() { + try { + return getWrappedEngine().getNeedClientAuth(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public void setWantClientAuth(boolean b) { + try { + getWrappedEngine().setWantClientAuth(b); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public boolean getWantClientAuth() { + try { + return getWrappedEngine().getWantClientAuth(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public void setEnableSessionCreation(boolean b) { + try { + getWrappedEngine().setEnableSessionCreation(b); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public boolean getEnableSessionCreation() { + try { + return getWrappedEngine().getEnableSessionCreation(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public SSLParameters getSSLParameters() { + try { + return getWrappedEngine().getSSLParameters(); + } finally { + assertErrorStackEmpty(); + } + } + + @Override + public void setSSLParameters(SSLParameters params) { + try { + getWrappedEngine().setSSLParameters(params); + } finally { + assertErrorStackEmpty(); + } + } + + public String getApplicationProtocol() { + if (PlatformDependent.javaVersion() >= 9) { + try { + return Java9SslUtils.getApplicationProtocol(getWrappedEngine()); + } finally { + assertErrorStackEmpty(); + } + } + throw new UnsupportedOperationException(); + } + + public String getHandshakeApplicationProtocol() { + if (PlatformDependent.javaVersion() >= 9) { + try { + return Java9SslUtils.getHandshakeApplicationProtocol(getWrappedEngine()); + } finally { + assertErrorStackEmpty(); + } + } + throw new UnsupportedOperationException(); + } + + public void setHandshakeApplicationProtocolSelector(BiFunction, String> selector) { + if (PlatformDependent.javaVersion() >= 9) { + try { + Java9SslUtils.setHandshakeApplicationProtocolSelector(getWrappedEngine(), selector); + } finally { + assertErrorStackEmpty(); + } + } + throw new UnsupportedOperationException(); + } + + public BiFunction, String> getHandshakeApplicationProtocolSelector() { + if (PlatformDependent.javaVersion() >= 9) { + try { + return Java9SslUtils.getHandshakeApplicationProtocolSelector(getWrappedEngine()); + } finally { + assertErrorStackEmpty(); + } + } + throw new UnsupportedOperationException(); + } + + @Override + public int refCnt() { + return getWrappedEngine().refCnt(); + } + + @Override + public OpenSslErrorStackAssertSSLEngine retain() { + getWrappedEngine().retain(); + return this; + } + + @Override + public OpenSslErrorStackAssertSSLEngine retain(int increment) { + getWrappedEngine().retain(increment); + return this; + } + + @Override + public OpenSslErrorStackAssertSSLEngine touch() { + getWrappedEngine().touch(); + return this; + } + + @Override + public OpenSslErrorStackAssertSSLEngine touch(Object hint) { + getWrappedEngine().touch(hint); + return this; + } + + @Override + public boolean release() { + return getWrappedEngine().release(); + } + + @Override + public boolean release(int decrement) { + return getWrappedEngine().release(); + } + + @Override + public String getNegotiatedApplicationProtocol() { + return getWrappedEngine().getNegotiatedApplicationProtocol(); + } + + @Override + void setNegotiatedApplicationProtocol(String applicationProtocol) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceCountedOpenSslEngine getWrappedEngine() { + return (ReferenceCountedOpenSslEngine) super.getWrappedEngine(); + } + + private static void assertErrorStackEmpty() { + Assert.assertEquals("SSL error stack non-empty", 0, SSL.getLastErrorNumber()); + } +} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java index f63a16feb5e7..d2a00c5432ce 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java @@ -19,6 +19,7 @@ import org.junit.Ignore; import org.junit.Test; +import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -111,4 +112,9 @@ protected boolean mySetupMutualAuthServerIsValidServerException(Throwable cause) // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. return super.mySetupMutualAuthServerIsValidServerException(cause) || causedBySSLException(cause); } + + @Override + protected SSLEngine wrapEngine(SSLEngine engine) { + return Java8SslTestUtils.wrapSSLEngineForTesting(engine); + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 9924a5f1f395..1e66efc6f8fb 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -909,7 +909,7 @@ private void mySetupMutualAuth( sb.channel(NioServerSocketChannel.class); sb.childHandler(new ChannelInitializer() { @Override - protected void initChannel(Channel ch) throws Exception { + protected void initChannel(Channel ch) { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ChannelPipeline p = ch.pipeline(); @@ -1054,7 +1054,7 @@ public void testGetCreationTime() throws Exception { .sslContextProvider(clientSslContextProvider()).build(); SSLEngine engine = null; try { - engine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + engine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); assertTrue(engine.getSession().getCreationTime() <= System.currentTimeMillis()); } finally { cleanupClientSslEngine(engine); @@ -1076,8 +1076,8 @@ public void testSessionInvalidate() throws Exception { SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { - clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); handshake(clientEngine, serverEngine); SSLSession session = serverEngine.getSession(); @@ -1106,8 +1106,8 @@ public void testSSLSessionId() throws Exception { SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { - clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); // Before the handshake the id should have length == 0 assertEquals(0, clientEngine.getSession().getId().length); @@ -1247,7 +1247,7 @@ protected void testEnablingAnAlreadyDisabledSslProtocol(String[] protocols1, Str .sslContextProvider(serverSslContextProvider()) .build(); - sslEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + sslEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); // Disable all protocols sslEngine.setEnabledProtocols(EmptyArrays.EMPTY_STRINGS); @@ -1597,13 +1597,13 @@ public void testUnwrapBehavior() throws Exception { .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); byte[] bytes = "Hello World".getBytes(CharsetUtil.US_ASCII); @@ -1688,14 +1688,14 @@ private void testProtocol(String[] clientProtocols, String[] serverProtocols) th .sslProvider(sslClientProvider()) .protocols(clientProtocols) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .protocols(serverProtocols) .build(); - SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { handshake(client, server); @@ -1727,8 +1727,8 @@ public void testHandshakeCompletesWithNonContiguousProtocolsTLSv1_2CipherOnly() SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { - clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); handshake(clientEngine, serverEngine); } finally { cleanupClientSslEngine(clientEngine); @@ -1758,8 +1758,8 @@ public void testHandshakeCompletesWithoutFilteringSupportedCipher() throws Excep SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { - clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); handshake(clientEngine, serverEngine); } finally { cleanupClientSslEngine(clientEngine); @@ -1777,13 +1777,13 @@ public void testPacketBufferSizeLimit() throws Exception { .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { // Allocate an buffer that is bigger then the max plain record size. @@ -1816,7 +1816,7 @@ public void testSSLEngineUnwrapNoSslRecord() throws Exception { .forClient() .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { ByteBuffer src = allocateBuffer(client.getSession().getApplicationBufferSize()); @@ -1844,7 +1844,7 @@ public void testBeginHandshakeAfterEngineClosed() throws SSLException { .forClient() .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { client.closeInbound(); @@ -1868,13 +1868,13 @@ public void testBeginHandshakeCloseOutbound() throws Exception { .forClient() .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { testBeginHandshakeCloseOutbound(client); @@ -1915,13 +1915,13 @@ public void testCloseInboundAfterBeginHandshake() throws Exception { .forClient() .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { testCloseInboundAfterBeginHandshake(client); @@ -1952,13 +1952,13 @@ public void testCloseNotifySequence() throws Exception { .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { ByteBuffer plainClientOut = allocateBuffer(client.getSession().getApplicationBufferSize()); @@ -2093,13 +2093,13 @@ public void testWrapAfterCloseOutbound() throws Exception { .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { ByteBuffer dst = allocateBuffer(client.getSession().getPacketBufferSize()); @@ -2132,13 +2132,13 @@ public void testMultipleRecordsInOneBufferWithNonZeroPosition() throws Exception .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { // Choose buffer size small enough that we can put multiple buffers into one buffer and pass it into the @@ -2207,13 +2207,13 @@ public void testMultipleRecordsInOneBufferBiggerThenPacketBufferSize() throws Ex .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { ByteBuffer plainClientOut = allocateBuffer(4096); @@ -2257,13 +2257,13 @@ public void testBufferUnderFlow() throws Exception { .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { ByteBuffer plainClient = allocateBuffer(1024); @@ -2328,13 +2328,13 @@ public void testWrapDoesNotZeroOutSrc() throws Exception { .trustManager(cert.cert()) .sslProvider(sslClientProvider()) .build(); - SSLEngine client = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { ByteBuffer plainServerOut = allocateBuffer(server.getSession().getApplicationBufferSize() / 2); @@ -2380,7 +2380,7 @@ private void testDisableProtocols(String protocol, String... disabledProtocols) .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) .build(); - SSLEngine server = ctx.newEngine(UnpooledByteBufAllocator.DEFAULT); + SSLEngine server = wrapEngine(ctx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { Set supported = new HashSet(Arrays.asList(server.getSupportedProtocols())); @@ -2388,10 +2388,10 @@ private void testDisableProtocols(String protocol, String... disabledProtocols) server.setEnabledProtocols(server.getSupportedProtocols()); Assert.assertEquals(supported, new HashSet(Arrays.asList(server.getSupportedProtocols()))); - for (String disabled: disabledProtocols) { + for (String disabled : disabledProtocols) { supported.remove(disabled); } - if (supported.contains(SslUtils.PROTOCOL_SSL_V2_HELLO) && supported.size() == 1) { + if (supported.contains(PROTOCOL_SSL_V2_HELLO) && supported.size() == 1) { // It's not allowed to set only PROTOCOL_SSL_V2_HELLO if using JDK SSLEngine. return; } @@ -2405,4 +2405,8 @@ private void testDisableProtocols(String protocol, String... disabledProtocols) cert.delete(); } } + + protected SSLEngine wrapEngine(SSLEngine engine) { + return engine; + } } From 0bce0450c05697df6ff77a0a14183ee53a00053f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 16 May 2018 18:58:27 +0200 Subject: [PATCH 010/417] Adjust tests to also pass when using BoringSSL (#7946) Motivation: Some of the tests failed when using BoringSSL as some protocol / cipher combinations are not supported and it uses a different alert when the cert is not valid yet. Modification: - Remove protocol / cipher combos that are not supported by BoringSSL - Test for different alert when using BoringSSL Result: Not test failures when using BoringSSL. --- .../netty/handler/ssl/OpenSslEngineTest.java | 3 --- .../io/netty/handler/ssl/SslErrorTest.java | 18 +++++++++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index 9949dd918993..944b22b028aa 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -558,9 +558,7 @@ public void testWrapWithDifferentSizesSSLv3() throws Exception { .build(); testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "ADH-AES128-SHA"); - testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "ADH-CAMELLIA128-SHA"); - testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "DES-CBC3-SHA"); testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "AECDH-AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "AECDH-DES-CBC3-SHA"); testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "CAMELLIA128-SHA"); @@ -568,7 +566,6 @@ public void testWrapWithDifferentSizesSSLv3() throws Exception { testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "SEED-SHA"); testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "RC4-MD5"); testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "ADH-AES256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "ADH-SEED-SHA"); testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "ADH-DES-CBC3-SHA"); testWrapWithDifferentSizes(PROTOCOL_SSL_V3, "EDH-RSA-DES-CBC3-SHA"); diff --git a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java index 27aa9bfe16d7..6285ae0c4ef3 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java @@ -203,14 +203,24 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { if (reason == CertPathValidatorException.BasicReason.EXPIRED) { verifyException(unwrappedCause, "expired", promise); } else if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) { - verifyException(unwrappedCause, "bad", promise); + // BoringSSL uses "expired" in this case while others use "bad" + if ("BoringSSL".equals(OpenSsl.versionString())) { + verifyException(unwrappedCause, "expired", promise); + } else { + verifyException(unwrappedCause, "bad", promise); + } } else if (reason == CertPathValidatorException.BasicReason.REVOKED) { verifyException(unwrappedCause, "revoked", promise); } } else if (exception instanceof CertificateExpiredException) { verifyException(unwrappedCause, "expired", promise); } else if (exception instanceof CertificateNotYetValidException) { - verifyException(unwrappedCause, "bad", promise); + // BoringSSL uses "expired" in this case while others use "bad" + if ("BoringSSL".equals(OpenSsl.versionString())) { + verifyException(unwrappedCause, "expired", promise); + } else { + verifyException(unwrappedCause, "bad", promise); + } } else if (exception instanceof CertificateRevokedException) { verifyException(unwrappedCause, "revoked", promise); } @@ -242,7 +252,9 @@ private static void verifyException(Throwable cause, String messagePart, Promise if (message.toLowerCase(Locale.UK).contains(messagePart.toLowerCase(Locale.UK))) { promise.setSuccess(null); } else { - promise.setFailure(new AssertionError("message not contains '" + messagePart + "': " + message)); + Throwable error = new AssertionError("message not contains '" + messagePart + "': " + message); + error.initCause(cause); + promise.setFailure(error); } } From 47985c11c1d9f11b94018539108ed65f0c6a32ab Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 16 May 2018 20:01:58 +0200 Subject: [PATCH 011/417] Add missing parameter when delegate to SSLEngine. Motivation: https://github.com/netty/netty/pull/7943 had a bug which caused to not have the argument passed to the delegating method. Modifications: Add argument to release call. Result: Correctly delegate method. --- .../io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java index 8d43612584bf..af71f72570a7 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java @@ -416,7 +416,7 @@ public boolean release() { @Override public boolean release(int decrement) { - return getWrappedEngine().release(); + return getWrappedEngine().release(decrement); } @Override From 546ddd2c2899b8e410ddfac6b3987d4108b65c17 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 16 May 2018 20:08:05 +0200 Subject: [PATCH 012/417] Correctly calculate and respect if we can correctly fullfil wrap for alerts. (#7945) Motivation: We previously did not correctly take into account when we could not wrap (and so produce) the full SSL record with an alert when the SSLEngine was closed. There are two problems here: - If we call wrap(...) with an empty dst buffer after closeOutbound() was called we will not notify the user if we could not store the whole SSLRecord into the dst buffer and so we may produce incomplete SSLRecords Modifications: Add unit test which failed before. Result: Correctly handle the case when the dst buffer is not big enough and and alert needs to be produced. --- .../ssl/ReferenceCountedOpenSslEngine.java | 11 +++- .../netty/handler/ssl/OpenSslEngineTest.java | 63 ++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index aa03cf0a2735..21770f128cab 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -607,8 +607,17 @@ public final SSLEngineResult wrap( int bioLengthBefore = SSL.bioLengthByteBuffer(networkBIO); - // Explicit use outboundClosed as we want to drain any bytes that are still present. + // Explicitly use outboundClosed as we want to drain any bytes that are still present. if (outboundClosed) { + // If the outbound was closed we want to ensure we can produce the alert to the destination buffer. + // This is true even if we not using jdkCompatibilityMode. + // + // We use a plaintextLength of 2 as we at least want to have an alert fit into it. + // https://tools.ietf.org/html/rfc5246#section-7.2 + if (!isBytesAvailableEnoughForWrap(dst.remaining(), 2, 1)) { + return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0); + } + // There is something left to drain. // See https://github.com/netty/netty/issues/6260 bytesProduced = SSL.bioFlushByteBuffer(networkBIO); diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index 944b22b028aa..ec2d4bc33bcc 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -23,7 +23,6 @@ import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.internal.tcnative.SSL; import io.netty.util.internal.PlatformDependent; -import org.junit.Assert; import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; @@ -401,6 +400,68 @@ public void testCalculateOutNetBufSize0() throws SSLException { } } + @Test + public void testCorrectlyCalculateSpaceForAlert() throws Exception { + testCorrectlyCalculateSpaceForAlert(true); + } + + @Test + public void testCorrectlyCalculateSpaceForAlertJDKCompatabilityModeOff() throws Exception { + testCorrectlyCalculateSpaceForAlert(false); + } + + private void testCorrectlyCalculateSpaceForAlert(boolean jdkCompatabilityMode) throws Exception { + SelfSignedCertificate ssc = new SelfSignedCertificate(); + serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) + .sslProvider(sslServerProvider()) + .build(); + + clientSslCtx = SslContextBuilder.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .build(); + SSLEngine clientEngine = null; + SSLEngine serverEngine = null; + try { + if (jdkCompatabilityMode) { + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + } else { + clientEngine = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); + serverEngine = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); + } + handshake(clientEngine, serverEngine); + + // This should produce an alert + clientEngine.closeOutbound(); + + ByteBuffer empty = allocateBuffer(0); + ByteBuffer dst = allocateBuffer(clientEngine.getSession().getPacketBufferSize()); + // Limit to something that is guaranteed to be too small to hold a SSL Record. + dst.limit(1); + + // As we called closeOutbound() before this should produce a BUFFER_OVERFLOW. + SSLEngineResult result = clientEngine.wrap(empty, dst); + assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); + + // This must calculate a length that can hold an alert at least (or more). + dst.limit(dst.capacity()); + + result = clientEngine.wrap(empty, dst); + assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); + + // flip the buffer so we can verify we produced a full length buffer. + dst.flip(); + + int length = SslUtils.getEncryptedPacketLength(new ByteBuffer[] { dst }, 0); + assertEquals(length, dst.remaining()); + } finally { + cleanupClientSslEngine(clientEngine); + cleanupServerSslEngine(serverEngine); + ssc.delete(); + } + } + @Override protected void mySetupMutualAuthServerInitSslHandler(SslHandler handler) { ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) handler.engine(); From c3d29f7b9ef138aecaff8b1c6cd631d742d6b22a Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 17 May 2018 06:55:48 +0200 Subject: [PATCH 013/417] Guard against calling malloc(0) when create ByteBuffer. (#7948) Motivation: We did not guard against the case of calling malloc(0) when creating a ByteBuffer without a Cleaner. The problem is that malloc(0) can have different behaviour, it either return a null-pointer or a valid pointer that you can pass to free. The real problem arise if Unsafe.allocateMemory(0) returns 0 and we use it as the memoryAddress of the ByteBuffer. The problem here is that native libraries test for 0 and handle it as a null-ptr. This is for example true in SSL.bioSetByteBuffer(...) which would throw a NPE when 0 is used as memoryAddress and so produced errors during SSL usage. Modifications: - Always allocate 1 byte as minimum (even if we ask for an empty buffer). - Add unit test. Result: No more errors possible because of malloc(0). --- .../netty/util/internal/PlatformDependent0.java | 5 ++++- .../util/internal/PlatformDependentTest.java | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java index 275227b5f05e..7d56356cc4bb 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java @@ -427,7 +427,10 @@ static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) { } static ByteBuffer allocateDirectNoCleaner(int capacity) { - return newDirectBuffer(UNSAFE.allocateMemory(capacity), capacity); + // Calling malloc with capacity of 0 may return a null ptr or a memory address that can be used. + // Just use 1 to make it safe to use in all cases: + // See: http://pubs.opengroup.org/onlinepubs/009695399/functions/malloc.html + return newDirectBuffer(UNSAFE.allocateMemory(Math.max(1, capacity)), capacity); } static boolean hasAllocateArrayMethod() { diff --git a/common/src/test/java/io/netty/util/internal/PlatformDependentTest.java b/common/src/test/java/io/netty/util/internal/PlatformDependentTest.java index 3a747deb6158..295fe30a14c6 100644 --- a/common/src/test/java/io/netty/util/internal/PlatformDependentTest.java +++ b/common/src/test/java/io/netty/util/internal/PlatformDependentTest.java @@ -17,14 +17,13 @@ import org.junit.Test; +import java.nio.ByteBuffer; import java.util.Random; import static io.netty.util.internal.PlatformDependent.hashCodeAscii; import static io.netty.util.internal.PlatformDependent.hashCodeAsciiSafe; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; public class PlatformDependentTest { private static final Random r = new Random(); @@ -146,4 +145,13 @@ public void testHashCodeAscii() { hashCodeAscii(string)); } } + + @Test + public void testAllocateWithCapacity0() { + assumeTrue(PlatformDependent.hasDirectBufferNoCleanerConstructor()); + ByteBuffer buffer = PlatformDependent.allocateDirectNoCleaner(0); + assertNotEquals(0, PlatformDependent.directBufferAddress(buffer)); + assertEquals(0, buffer.capacity()); + PlatformDependent.freeDirectNoCleaner(buffer); + } } From 19e7b4438fd5674c893ac918a1f1c0ef3910ed82 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 18 May 2018 12:27:40 +0200 Subject: [PATCH 014/417] Update to conscrypt 1.1.2 (#7949) Motivation: We use latest conscrypt to test against. Modifications: Update to conscrypt 1.1.2 Result: Use latest conscrypt release. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7dbcefc49190..caf9cb5f312c 100644 --- a/pom.xml +++ b/pom.xml @@ -225,7 +225,7 @@ ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber - 1.0.1 + 1.1.2 ${os.detected.name}-${os.detected.arch} ${project.basedir}/../common/src/test/resources/logback-test.xml From 8ae126aaa81491527dfd03cf725a8e60eeb9ed96 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 18 May 2018 12:27:56 +0200 Subject: [PATCH 015/417] Revert workaround in test for Java 11 as it produces a connection-reset as expected now. (#7951) Motivation: We added a workaround for Java 11 as it not produced a connect-reset when SO_LINGER with 0 was set and NIO was used. This was fixed in the latest ea release of Java 11: - http://hg.openjdk.java.net/jdk/jdk/rev/ea54197f4fe4 - https://bugs.openjdk.java.net/browse/JDK-8203059 Modifications: Revert workaround. Result: Test that Java 11 behave the same way as earlier Java versions again. --- .../netty/testsuite/transport/socket/SocketRstTest.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketRstTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketRstTest.java index 88cef1236a67..4cde8b023bb3 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketRstTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketRstTest.java @@ -22,8 +22,6 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.util.internal.PlatformDependent; import org.junit.Test; import java.io.IOException; @@ -91,13 +89,6 @@ public void channelInactive(ChannelHandlerContext ctx) { // Verify the client received a RST. Throwable cause = throwableRef.get(); - if (PlatformDependent.javaVersion() >= 11 && sb.config().group() instanceof NioEventLoopGroup) { - // In Java11 calling SocketChannel.close() will also call shutdown(..,SHUT_WR) before actual closing the - // fd which means we may not see the ECONNRESET at all :( - if (cause == null) { - return; - } - } assertTrue("actual [type, message]: [" + cause.getClass() + ", " + cause.getMessage() + "]", cause instanceof IOException); From 7727649b2ca08010118a8c57b2c0a5702cf876f2 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 18 May 2018 19:36:40 +0200 Subject: [PATCH 016/417] Add tests for the Conscrypt based SSLEngine. (#7950) Motivation: We currently have only interopt tests for Conscrypt, we should also have non-interopt tests. Modifications: Add ConscryptSslEngineTest Result: More tests --- .../handler/ssl/ConscryptSslEngineTest.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java new file mode 100644 index 000000000000..e57fd58be0fd --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java @@ -0,0 +1,80 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.security.Provider; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.junit.Assume.assumeTrue; + +@RunWith(Parameterized.class) +public class ConscryptSslEngineTest extends SSLEngineTest { + + @Parameterized.Parameters(name = "{index}: bufferType = {0}") + public static Collection data() { + List params = new ArrayList(); + for (BufferType type: BufferType.values()) { + params.add(type); + } + return params; + } + + public ConscryptSslEngineTest(BufferType type) { + super(type); + } + + @BeforeClass + public static void checkConscrypt() { + assumeTrue(Conscrypt.isAvailable()); + } + + @Override + protected SslProvider sslClientProvider() { + return SslProvider.JDK; + } + + @Override + protected SslProvider sslServerProvider() { + return SslProvider.JDK; + } + + @Override + protected Provider clientSslContextProvider() { + return Java8SslTestUtils.conscryptProvider(); + } + + @Override + protected Provider serverSslContextProvider() { + return Java8SslTestUtils.conscryptProvider(); + } + + @Ignore /* Does the JDK support a "max certificate chain length"? */ + @Override + public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth() { + } + + @Ignore /* Does the JDK support a "max certificate chain length"? */ + @Override + public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth() { + } +} From 987c443888ce2c47144ce0130c25b356416e2925 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 18 May 2018 19:36:57 +0200 Subject: [PATCH 017/417] Use ByteBufAllocator used by the ReferenceCountedOpenSslEngine when build key-material. (#7952) Motivation: When we build the key-material we should use the ByteBufAllocator used by the ReferenceCountedOpenSslEngine when possible. Modifications: Whenever we have access to the ReferenceCountedOpenSslEngine we use its allocator. Result: Use correct allocator --- .../main/java/io/netty/handler/ssl/OpenSsl.java | 4 ++-- .../handler/ssl/OpenSslKeyMaterialManager.java | 16 ++++++++-------- .../ssl/ReferenceCountedOpenSslContext.java | 8 +++----- .../ssl/ReferenceCountedOpenSslEngine.java | 2 +- .../ReferenceCountedOpenSslServerContext.java | 3 ++- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index 6ad934fbc652..27753d2284d5 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -17,6 +17,7 @@ package io.netty.handler.ssl; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.internal.tcnative.Buffer; import io.netty.internal.tcnative.Library; @@ -33,7 +34,6 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; @@ -157,7 +157,7 @@ public final class OpenSsl { } try { cert = new SelfSignedCertificate(); - certBio = ReferenceCountedOpenSslContext.toBIO(cert.cert()); + certBio = ReferenceCountedOpenSslContext.toBIO(ByteBufAllocator.DEFAULT, cert.cert()); SSL.setCertificateChainBio(ssl, certBio, false); supportsKeyManagerFactory = true; try { diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java index 2e48e8b04be8..fd2a7ee8b804 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java @@ -79,7 +79,7 @@ void setKeyMaterial(ReferenceCountedOpenSslEngine engine) throws SSLException { if (type != null) { String alias = chooseServerAlias(engine, type); if (alias != null && aliases.add(alias)) { - setKeyMaterial(ssl, alias); + setKeyMaterial(ssl, alias, engine.alloc); } } } @@ -101,10 +101,10 @@ CertificateRequestedCallback.KeyMaterial keyMaterial(ReferenceCountedOpenSslEngi } PrivateKey key = keyManager.getPrivateKey(alias); - keyCertChainBio = toBIO(certificates); + keyCertChainBio = toBIO(engine.alloc, certificates); certChain = SSL.parseX509Chain(keyCertChainBio); if (key != null) { - keyBio = toBIO(key); + keyBio = toBIO(engine.alloc, key); pkey = SSL.parsePrivateKey(keyBio, password); } CertificateRequestedCallback.KeyMaterial material = new CertificateRequestedCallback.KeyMaterial( @@ -127,7 +127,7 @@ CertificateRequestedCallback.KeyMaterial keyMaterial(ReferenceCountedOpenSslEngi } } - private void setKeyMaterial(long ssl, String alias) throws SSLException { + private void setKeyMaterial(long ssl, String alias, ByteBufAllocator allocator) throws SSLException { long keyBio = 0; long keyCertChainBio = 0; long keyCertChainBio2 = 0; @@ -142,13 +142,13 @@ private void setKeyMaterial(long ssl, String alias) throws SSLException { PrivateKey key = keyManager.getPrivateKey(alias); // Only encode one time - PemEncoded encoded = PemX509Certificate.toPEM(ByteBufAllocator.DEFAULT, true, certificates); + PemEncoded encoded = PemX509Certificate.toPEM(allocator, true, certificates); try { - keyCertChainBio = toBIO(ByteBufAllocator.DEFAULT, encoded.retain()); - keyCertChainBio2 = toBIO(ByteBufAllocator.DEFAULT, encoded.retain()); + keyCertChainBio = toBIO(allocator, encoded.retain()); + keyCertChainBio2 = toBIO(allocator, encoded.retain()); if (key != null) { - keyBio = toBIO(key); + keyBio = toBIO(allocator, key); } SSL.setCertificateBio(ssl, keyCertChainBio, keyBio, password); diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index a695d2d689ac..84cf6da0a787 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -710,7 +710,7 @@ static void setKeyMaterial(long ctx, X509Certificate[] keyCertChain, PrivateKey keyCertChainBio2 = toBIO(ByteBufAllocator.DEFAULT, encoded.retain()); if (key != null) { - keyBio = toBIO(key); + keyBio = toBIO(ByteBufAllocator.DEFAULT, key); } SSLContext.setCertificateBio( @@ -742,12 +742,11 @@ static void freeBio(long bio) { * Return the pointer to a in-memory BIO * or {@code 0} if the {@code key} is {@code null}. The BIO contains the content of the {@code key}. */ - static long toBIO(PrivateKey key) throws Exception { + static long toBIO(ByteBufAllocator allocator, PrivateKey key) throws Exception { if (key == null) { return 0; } - ByteBufAllocator allocator = ByteBufAllocator.DEFAULT; PemEncoded pem = PemPrivateKey.toPEM(allocator, true, key); try { return toBIO(allocator, pem.retain()); @@ -760,7 +759,7 @@ static long toBIO(PrivateKey key) throws Exception { * Return the pointer to a in-memory BIO * or {@code 0} if the {@code certChain} is {@code null}. The BIO contains the content of the {@code certChain}. */ - static long toBIO(X509Certificate... certChain) throws Exception { + static long toBIO(ByteBufAllocator allocator, X509Certificate... certChain) throws Exception { if (certChain == null) { return 0; } @@ -769,7 +768,6 @@ static long toBIO(X509Certificate... certChain) throws Exception { throw new IllegalArgumentException("certChain can't be empty"); } - ByteBufAllocator allocator = ByteBufAllocator.DEFAULT; PemEncoded pem = PemX509Certificate.toPEM(allocator, true, certChain); try { return toBIO(allocator, pem.retain()); diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 21770f128cab..788a620f918f 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -209,7 +209,7 @@ protected void deallocate() { final boolean jdkCompatibilityMode; private final boolean clientMode; - private final ByteBufAllocator alloc; + final ByteBufAllocator alloc; private final OpenSslEngineMap engineMap; private final OpenSslApplicationProtocolNegotiator apn; private final OpenSslSession session; diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java index 4c9df3148c0c..8eb4940c401c 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java @@ -15,6 +15,7 @@ */ package io.netty.handler.ssl; +import io.netty.buffer.ByteBufAllocator; import io.netty.internal.tcnative.SSL; import io.netty.internal.tcnative.SSLContext; import io.netty.internal.tcnative.SniHostNameMatcher; @@ -162,7 +163,7 @@ static ServerContext newSessionContext(ReferenceCountedOpenSslContext thiz, long if (issuers != null && issuers.length > 0) { long bio = 0; try { - bio = toBIO(issuers); + bio = toBIO(ByteBufAllocator.DEFAULT, issuers); if (!SSLContext.setCACertificateBio(ctx, bio)) { throw new SSLException("unable to setup accepted issuers for trustmanager " + manager); } From 88f0586a7e89a4e35e0011bdf607b3192bfe7b5f Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Fri, 18 May 2018 23:31:59 -0700 Subject: [PATCH 018/417] Remove HpackDecoder.maxHeaderListSizeGoAway (#7911) Motivation: When a sender sends too large of headers it should not unnecessarily kill the connection, as killing the connection is a heavy-handed solution while SETTINGS_MAX_HEADER_LIST_SIZE is advisory and may be ignored. The maxHeaderListSizeGoAway limit in HpackDecoder is unnecessary because any headers causing the list to exceeding the max size can simply be thrown away. In addition, DefaultHttp2FrameReader.HeadersBlockBuilder limits the entire block to maxHeaderListSizeGoAway. Thus individual literals are limited to maxHeaderListSizeGoAway. (Technically, literals are limited to 1.6x maxHeaderListSizeGoAway, since the canonical Huffman code has a maximum compression ratio of .625. However, the "unnecessary" limit in HpackDecoder was also being applied to compressed sizes.) Modifications: Remove maxHeaderListSizeGoAway checking in HpackDecoder and instead eagerly throw away any headers causing the list to exceed maxHeaderListSize. Result: Fewer large header cases will trigger connection-killing. DefaultHttp2FrameReader.HeadersBlockBuilder will still kill the connection when maxHeaderListSizeGoAway is exceeded, however. Fixes #7887 --- .../http2/DefaultHttp2HeadersDecoder.java | 13 +- .../handler/codec/http2/HpackDecoder.java | 120 +++++++++--------- .../handler/codec/http2/HpackDecoderTest.java | 28 ++-- .../handler/codec/http2/HpackEncoderTest.java | 2 +- 4 files changed, 91 insertions(+), 72 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java index 684fef03528c..5d6320950ca3 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java @@ -22,6 +22,7 @@ import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_INITIAL_HUFFMAN_DECODE_CAPACITY; import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR; +import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; import static io.netty.handler.codec.http2.Http2Exception.connectionError; @UnstableApi @@ -31,6 +32,7 @@ public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2Hea private final HpackDecoder hpackDecoder; private final boolean validateHeaders; + private long maxHeaderListSizeGoAway; /** * Used to calculate an exponential moving average of header sizes to get an estimate of how large the data @@ -79,6 +81,8 @@ public DefaultHttp2HeadersDecoder(boolean validateHeaders, long maxHeaderListSiz DefaultHttp2HeadersDecoder(boolean validateHeaders, HpackDecoder hpackDecoder) { this.hpackDecoder = ObjectUtil.checkNotNull(hpackDecoder, "hpackDecoder"); this.validateHeaders = validateHeaders; + this.maxHeaderListSizeGoAway = + Http2CodecUtil.calculateMaxHeaderListSizeGoAway(hpackDecoder.getMaxHeaderListSize()); } @Override @@ -93,7 +97,12 @@ public long maxHeaderTableSize() { @Override public void maxHeaderListSize(long max, long goAwayMax) throws Http2Exception { - hpackDecoder.setMaxHeaderListSize(max, goAwayMax); + if (goAwayMax < max || goAwayMax < 0) { + throw connectionError(INTERNAL_ERROR, "Header List Size GO_AWAY %d must be non-negative and >= %d", + goAwayMax, max); + } + hpackDecoder.setMaxHeaderListSize(max); + this.maxHeaderListSizeGoAway = goAwayMax; } @Override @@ -103,7 +112,7 @@ public long maxHeaderListSize() { @Override public long maxHeaderListSizeGoAway() { - return hpackDecoder.getMaxHeaderListSizeGoAway(); + return maxHeaderListSizeGoAway; } @Override diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java index 67d6aa9944bf..078c0798918f 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java @@ -42,7 +42,6 @@ import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_HEADER_TABLE_SIZE; import static io.netty.handler.codec.http2.Http2CodecUtil.headerListSizeExceeded; import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR; -import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; import static io.netty.handler.codec.http2.Http2Exception.connectionError; import static io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName.getPseudoHeader; @@ -84,7 +83,6 @@ final class HpackDecoder { private final HpackDynamicTable hpackDynamicTable; private final HpackHuffmanDecoder hpackHuffmanDecoder; - private long maxHeaderListSizeGoAway; private long maxHeaderListSize; private long maxDynamicTableSize; private long encoderMaxDynamicTableSize; @@ -108,7 +106,6 @@ final class HpackDecoder { */ HpackDecoder(long maxHeaderListSize, int initialHuffmanDecodeCapacity, int maxHeaderTableSize) { this.maxHeaderListSize = checkPositive(maxHeaderListSize, "maxHeaderListSize"); - this.maxHeaderListSizeGoAway = Http2CodecUtil.calculateMaxHeaderListSizeGoAway(maxHeaderListSize); maxDynamicTableSize = encoderMaxDynamicTableSize = maxHeaderTableSize; maxDynamicTableSizeChangeRequired = false; @@ -122,8 +119,18 @@ final class HpackDecoder { * This method assumes the entire header block is contained in {@code in}. */ public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean validateHeaders) throws Http2Exception { + Http2HeadersSink sink = new Http2HeadersSink(headers, maxHeaderListSize); + decode(in, sink, validateHeaders); + + // we have read all of our headers. See if we have exceeded our maxHeaderListSize. We must + // delay throwing until this point to prevent dynamic table corruption + if (sink.exceededMaxLength()) { + headerListSizeExceeded(streamId, maxHeaderListSize, true); + } + } + + private void decode(ByteBuf in, Sink sink, boolean validateHeaders) throws Http2Exception { int index = 0; - long headersLength = 0; int nameLength = 0; int valueLength = 0; byte state = READ_HEADER_REPRESENTATION; @@ -151,8 +158,7 @@ public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean valid default: HpackHeaderField indexedHeader = getIndexedHeader(index); headerType = validate(indexedHeader.name, headerType, validateHeaders); - headersLength = addHeader(headers, indexedHeader.name, indexedHeader.value, - headersLength); + sink.appendToHeaderList(indexedHeader.name, indexedHeader.value); } } else if ((b & 0x40) == 0x40) { // Literal Header Field with Incremental Indexing @@ -193,11 +199,11 @@ public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean valid state = READ_INDEXED_HEADER_NAME; break; default: - // Index was stored as the prefix - name = readName(index); + // Index was stored as the prefix + name = readName(index); headerType = validate(name, headerType, validateHeaders); - nameLength = name.length(); - state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; + nameLength = name.length(); + state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; } } break; @@ -210,7 +216,7 @@ public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean valid case READ_INDEXED_HEADER: HpackHeaderField indexedHeader = getIndexedHeader(decodeULE128(in, index)); headerType = validate(indexedHeader.name, headerType, validateHeaders); - headersLength = addHeader(headers, indexedHeader.name, indexedHeader.value, headersLength); + sink.appendToHeaderList(indexedHeader.name, indexedHeader.value); state = READ_HEADER_REPRESENTATION; break; @@ -229,9 +235,6 @@ public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean valid if (index == 0x7f) { state = READ_LITERAL_HEADER_NAME_LENGTH; } else { - if (index > maxHeaderListSizeGoAway - headersLength) { - headerListSizeExceeded(maxHeaderListSizeGoAway); - } nameLength = index; state = READ_LITERAL_HEADER_NAME; } @@ -241,9 +244,6 @@ public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean valid // Header Name is a Literal String nameLength = decodeULE128(in, index); - if (nameLength > maxHeaderListSizeGoAway - headersLength) { - headerListSizeExceeded(maxHeaderListSizeGoAway); - } state = READ_LITERAL_HEADER_NAME; break; @@ -269,14 +269,10 @@ public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean valid break; case 0: headerType = validate(name, headerType, validateHeaders); - headersLength = insertHeader(headers, name, EMPTY_STRING, indexType, headersLength); + insertHeader(sink, name, EMPTY_STRING, indexType); state = READ_HEADER_REPRESENTATION; break; default: - // Check new header size against max header size - if ((long) index + nameLength > maxHeaderListSizeGoAway - headersLength) { - headerListSizeExceeded(maxHeaderListSizeGoAway); - } valueLength = index; state = READ_LITERAL_HEADER_VALUE; } @@ -287,10 +283,6 @@ public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean valid // Header Value is a Literal String valueLength = decodeULE128(in, index); - // Check new header size against max header size - if ((long) valueLength + nameLength > maxHeaderListSizeGoAway - headersLength) { - headerListSizeExceeded(maxHeaderListSizeGoAway); - } state = READ_LITERAL_HEADER_VALUE; break; @@ -302,7 +294,7 @@ public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean valid CharSequence value = readStringLiteral(in, valueLength, huffmanEncoded); headerType = validate(name, headerType, validateHeaders); - headersLength = insertHeader(headers, name, value, indexType, headersLength); + insertHeader(sink, name, value, indexType); state = READ_HEADER_REPRESENTATION; break; @@ -311,13 +303,6 @@ public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean valid } } - // we have read all of our headers, and not exceeded maxHeaderListSizeGoAway see if we have - // exceeded our actual maxHeaderListSize. This must be done here to prevent dynamic table - // corruption - if (headersLength > maxHeaderListSize) { - headerListSizeExceeded(streamId, maxHeaderListSize, true); - } - if (state != READ_HEADER_REPRESENTATION) { throw connectionError(COMPRESSION_ERROR, "Incomplete header block fragment."); } @@ -341,27 +326,27 @@ public void setMaxHeaderTableSize(long maxHeaderTableSize) throws Http2Exception } } + /** + * @deprecated use {@link #setmaxHeaderListSize(long)}; {@code maxHeaderListSizeGoAway} is + * ignored + */ + @Deprecated public void setMaxHeaderListSize(long maxHeaderListSize, long maxHeaderListSizeGoAway) throws Http2Exception { - if (maxHeaderListSizeGoAway < maxHeaderListSize || maxHeaderListSizeGoAway < 0) { - throw connectionError(INTERNAL_ERROR, "Header List Size GO_AWAY %d must be positive and >= %d", - maxHeaderListSizeGoAway, maxHeaderListSize); - } + setMaxHeaderListSize(maxHeaderListSize); + } + + public void setMaxHeaderListSize(long maxHeaderListSize) throws Http2Exception { if (maxHeaderListSize < MIN_HEADER_LIST_SIZE || maxHeaderListSize > MAX_HEADER_LIST_SIZE) { throw connectionError(PROTOCOL_ERROR, "Header List Size must be >= %d and <= %d but was %d", MIN_HEADER_TABLE_SIZE, MAX_HEADER_TABLE_SIZE, maxHeaderListSize); } this.maxHeaderListSize = maxHeaderListSize; - this.maxHeaderListSizeGoAway = maxHeaderListSizeGoAway; } public long getMaxHeaderListSize() { return maxHeaderListSize; } - public long getMaxHeaderListSizeGoAway() { - return maxHeaderListSizeGoAway; - } - /** * Return the maximum table size. This is the maximum size allowed by both the encoder and the * decoder. @@ -450,9 +435,9 @@ private HpackHeaderField getIndexedHeader(int index) throws Http2Exception { throw INDEX_HEADER_ILLEGAL_INDEX_VALUE; } - private long insertHeader(Http2Headers headers, CharSequence name, CharSequence value, - IndexType indexType, long headerSize) throws Http2Exception { - headerSize = addHeader(headers, name, value, headerSize); + private void insertHeader(Sink sink, CharSequence name, CharSequence value, + IndexType indexType) throws Http2Exception { + sink.appendToHeaderList(name, value); switch (indexType) { case NONE: @@ -466,18 +451,6 @@ private long insertHeader(Http2Headers headers, CharSequence name, CharSequence default: throw new Error("should not reach here"); } - - return headerSize; - } - - private long addHeader(Http2Headers headers, CharSequence name, CharSequence value, long headersLength) - throws Http2Exception { - headersLength += HpackHeaderField.sizeOf(name, value); - if (headersLength > maxHeaderListSizeGoAway) { - headerListSizeExceeded(maxHeaderListSizeGoAway); - } - headers.add(name, value); - return headersLength; } private CharSequence readStringLiteral(ByteBuf in, int length, boolean huffmanEncoded) throws Http2Exception { @@ -553,4 +526,35 @@ private enum HeaderType { REQUEST_PSEUDO_HEADER, RESPONSE_PSEUDO_HEADER } + + private interface Sink { + void appendToHeaderList(CharSequence name, CharSequence value); + } + + private static final class Http2HeadersSink implements Sink { + private final Http2Headers headers; + private final long maxHeaderListSize; + private long headersLength; + private boolean exceededMaxLength; + + public Http2HeadersSink(Http2Headers headers, long maxHeaderListSize) { + this.headers = headers; + this.maxHeaderListSize = maxHeaderListSize; + } + + @Override + public void appendToHeaderList(CharSequence name, CharSequence value) { + headersLength += HpackHeaderField.sizeOf(name, value); + if (headersLength > maxHeaderListSize) { + exceededMaxLength = true; + } + if (!exceededMaxLength) { + headers.add(name, value); + } + } + + public boolean exceededMaxLength() { + return exceededMaxLength; + } + } } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDecoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDecoderTest.java index 4652a1fceb66..ba0117553ebb 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDecoderTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDecoderTest.java @@ -431,15 +431,13 @@ public void testLiteralNeverIndexedWithLargeValue() throws Http2Exception { } @Test - public void testDecodeLargerThanMaxHeaderListSizeButSmallerThanMaxHeaderListSizeUpdatesDynamicTable() - throws Http2Exception { + public void testDecodeLargerThanMaxHeaderListSizeUpdatesDynamicTable() throws Http2Exception { ByteBuf in = Unpooled.buffer(300); try { - hpackDecoder.setMaxHeaderListSize(200, 300); + hpackDecoder.setMaxHeaderListSize(200); HpackEncoder hpackEncoder = new HpackEncoder(true); // encode headers that are slightly larger than maxHeaderListSize - // but smaller than maxHeaderListSizeGoAway Http2Headers toEncode = new DefaultHttp2Headers(); toEncode.add("test_1", "1"); toEncode.add("test_2", "2"); @@ -447,8 +445,7 @@ public void testDecodeLargerThanMaxHeaderListSizeButSmallerThanMaxHeaderListSize toEncode.add("test_3", "3"); hpackEncoder.encodeHeaders(1, in, toEncode, NEVER_SENSITIVE); - // decode the headers, we should get an exception, but - // the decoded headers object should contain all of the headers + // decode the headers, we should get an exception Http2Headers decoded = new DefaultHttp2Headers(); try { hpackDecoder.decode(1, in, decoded, true); @@ -457,8 +454,18 @@ public void testDecodeLargerThanMaxHeaderListSizeButSmallerThanMaxHeaderListSize assertTrue(e instanceof Http2Exception.HeaderListSizeException); } - assertEquals(4, decoded.size()); - assertTrue(decoded.contains("test_3")); + // but the dynamic table should have been updated, so that later blocks + // can refer to earlier headers + in.clear(); + // 0x80, "indexed header field representation" + // index 62, the first (most recent) dynamic table entry + in.writeByte(0x80 | 62); + Http2Headers decoded2 = new DefaultHttp2Headers(); + hpackDecoder.decode(1, in, decoded2, true); + + Http2Headers golden = new DefaultHttp2Headers(); + golden.add("test_3", "3"); + assertEquals(golden, decoded2); } finally { in.release(); } @@ -468,11 +475,10 @@ public void testDecodeLargerThanMaxHeaderListSizeButSmallerThanMaxHeaderListSize public void testDecodeCountsNamesOnlyOnce() throws Http2Exception { ByteBuf in = Unpooled.buffer(200); try { - hpackDecoder.setMaxHeaderListSize(3500, 4000); + hpackDecoder.setMaxHeaderListSize(3500); HpackEncoder hpackEncoder = new HpackEncoder(true); // encode headers that are slightly larger than maxHeaderListSize - // but smaller than maxHeaderListSizeGoAway Http2Headers toEncode = new DefaultHttp2Headers(); toEncode.add(String.format("%03000d", 0).replace('0', 'f'), "value"); toEncode.add("accept", "value"); @@ -493,7 +499,7 @@ public void testAccountForHeaderOverhead() throws Exception { String headerName = "12345"; String headerValue = "56789"; long headerSize = headerName.length() + headerValue.length(); - hpackDecoder.setMaxHeaderListSize(headerSize, 100); + hpackDecoder.setMaxHeaderListSize(headerSize); HpackEncoder hpackEncoder = new HpackEncoder(true); Http2Headers toEncode = new DefaultHttp2Headers(); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackEncoderTest.java index b1dce05c38b3..de049a6e5066 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackEncoderTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackEncoderTest.java @@ -74,7 +74,7 @@ public void testWillEncode16MBHeaderByDefault() throws Http2Exception { try { hpackEncoder.encodeHeaders(0, buf, headersIn, Http2HeadersEncoder.NEVER_SENSITIVE); - hpackDecoder.setMaxHeaderListSize(bigHeaderSize + 1024, bigHeaderSize + 1024); + hpackDecoder.setMaxHeaderListSize(bigHeaderSize + 1024); hpackDecoder.decode(0, buf, headersOut, false); } finally { buf.release(); From 583fc272f26373ff5f51c0871d9730370cd39a3e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 21 May 2018 19:22:31 +0200 Subject: [PATCH 019/417] Fixed|SimpleChannelPool.close() should only return after complete. (#7927) Motivation: We need to ensure we only return from close() after all work is done as otherwise we may close the EventExecutor before we dispatched everything. Modifications: Correctly wait on operations to complete before return. Result: Fixes https://github.com/netty/netty/issues/7901. --- .../netty/channel/pool/FixedChannelPool.java | 57 ++++++++++++------- .../netty/channel/pool/SimpleChannelPool.java | 3 +- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java b/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java index 1c0ebcc5343b..16a8a7cc9a7b 100644 --- a/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java +++ b/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java @@ -20,6 +20,7 @@ import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.GlobalEventExecutor; import io.netty.util.concurrent.Promise; import io.netty.util.internal.ObjectUtil; import io.netty.util.internal.ThrowableUtil; @@ -437,27 +438,43 @@ public void acquired() { @Override public void close() { - executor.execute(new Runnable() { - @Override - public void run() { - if (!closed) { - closed = true; - for (;;) { - AcquireTask task = pendingAcquireQueue.poll(); - if (task == null) { - break; - } - ScheduledFuture f = task.timeoutFuture; - if (f != null) { - f.cancel(false); - } - task.promise.setFailure(new ClosedChannelException()); - } - acquiredChannelCount = 0; - pendingAcquireCount = 0; - FixedChannelPool.super.close(); + if (executor.inEventLoop()) { + close0(); + } else { + executor.submit(new Runnable() { + @Override + public void run() { + close0(); + } + }).awaitUninterruptibly(); + } + } + + private void close0() { + if (!closed) { + closed = true; + for (;;) { + AcquireTask task = pendingAcquireQueue.poll(); + if (task == null) { + break; + } + ScheduledFuture f = task.timeoutFuture; + if (f != null) { + f.cancel(false); } + task.promise.setFailure(new ClosedChannelException()); } - }); + acquiredChannelCount = 0; + pendingAcquireCount = 0; + + // Ensure we dispatch this on another Thread as close0 will be called from the EventExecutor and we need + // to ensure we will not block in a EventExecutor. + GlobalEventExecutor.INSTANCE.execute(new Runnable() { + @Override + public void run() { + FixedChannelPool.super.close(); + } + }); + } } } diff --git a/transport/src/main/java/io/netty/channel/pool/SimpleChannelPool.java b/transport/src/main/java/io/netty/channel/pool/SimpleChannelPool.java index d8dd0eae9409..6fcfd4443fac 100644 --- a/transport/src/main/java/io/netty/channel/pool/SimpleChannelPool.java +++ b/transport/src/main/java/io/netty/channel/pool/SimpleChannelPool.java @@ -394,7 +394,8 @@ public void close() { if (channel == null) { break; } - channel.close(); + // Just ignore any errors that are reported back from close(). + channel.close().awaitUninterruptibly(); } } } From 4d6b006fe64059b6b9eba9fbb835dc4a22d33393 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 24 May 2018 20:13:21 +0200 Subject: [PATCH 020/417] Correctly take status into account when compare DefaultHttpResponse (#7965) Motivation: DefaultHttpResponse did not respect its status when compute the hashCode and check for equality. Modifications: Correctly implement hashCode and equals Result: Fixes https://github.com/netty/netty/issues/7964. --- .../codec/http/DefaultHttpResponse.java | 19 +++++++++ .../codec/http/DefaultHttpResponseTest.java | 40 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpResponseTest.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpResponse.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpResponse.java index 86858108a277..d5b7cf0b7b9a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpResponse.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpResponse.java @@ -105,4 +105,23 @@ public HttpResponse setProtocolVersion(HttpVersion version) { public String toString() { return HttpMessageUtil.appendResponse(new StringBuilder(256), this).toString(); } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + status.hashCode(); + result = 31 * result + super.hashCode(); + return result; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof DefaultHttpResponse)) { + return false; + } + + DefaultHttpResponse other = (DefaultHttpResponse) o; + + return status.equals(other.status()) && super.equals(o); + } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpResponseTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpResponseTest.java new file mode 100644 index 000000000000..0a466c6bff97 --- /dev/null +++ b/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpResponseTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.http; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class DefaultHttpResponseTest { + + @Test + public void testNotEquals() { + HttpResponse ok = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); + HttpResponse notFound = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND); + assertNotEquals(ok, notFound); + assertNotEquals(ok.hashCode(), notFound.hashCode()); + } + + @Test + public void testEquals() { + HttpResponse ok = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); + HttpResponse ok2 = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); + assertEquals(ok, ok2); + assertEquals(ok.hashCode(), ok2.hashCode()); + } +} From 19d1f4ea628d5042ea93856d286cb31b453beda0 Mon Sep 17 00:00:00 2001 From: Nick Travers Date: Thu, 24 May 2018 11:27:29 -0700 Subject: [PATCH 021/417] Propagate pong frames in WebSocketProtocolHandler (#7955) Motivation: Currently, on recipt of a PongWebSocketFrame, the WebSocketProtocolHandler will drop the frame, rather than passing it along so it can be referenced by other handlers. Modifications: Add boolean field to WebSocketProtocolHandler to indicate whether Pong frames should be dropped or propagated, defaulting to "true" to preserve existing functionality. Add new constructors to the client and server implementations of WebSocketProtocolHandler that allow for overriding the behavior for the handling of Pong frames. Result: PongWebSocketFrames are passed along the channel, if specified. --- .../WebSocketClientProtocolHandler.java | 17 ++++ .../websocketx/WebSocketProtocolHandler.java | 24 ++++- .../WebSocketServerProtocolHandler.java | 7 ++ .../WebSocketProtocolHandlerTest.java | 89 +++++++++++++++++++ 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandlerTest.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java index 2ace7e8aa09e..7c85c57748df 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java @@ -151,6 +151,23 @@ public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version * {@code true} if close frames should not be forwarded and just close the channel */ public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker, boolean handleCloseFrames) { + this(handshaker, handleCloseFrames, true); + } + + /** + * Base constructor + * + * @param handshaker + * The {@link WebSocketClientHandshaker} which will be used to issue the handshake once the connection + * was established to the remote peer. + * @param handleCloseFrames + * {@code true} if close frames should not be forwarded and just close the channel + * @param dropPongFrames + * {@code true} if pong frames should not be forwarded + */ + public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker, boolean handleCloseFrames, + boolean dropPongFrames) { + super(dropPongFrames); this.handshaker = handshaker; this.handleCloseFrames = handleCloseFrames; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java index 7037ae346867..53532cafe98b 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java @@ -22,6 +22,27 @@ import java.util.List; abstract class WebSocketProtocolHandler extends MessageToMessageDecoder { + + private final boolean dropPongFrames; + + /** + * Creates a new {@link WebSocketProtocolHandler} that will drop {@link PongWebSocketFrame}s. + */ + WebSocketProtocolHandler() { + this(true); + } + + /** + * Creates a new {@link WebSocketProtocolHandler}, given a parameter that determines whether or not to drop {@link + * PongWebSocketFrame}s. + * + * @param dropPongFrames + * {@code true} if {@link PongWebSocketFrame}s should be dropped + */ + WebSocketProtocolHandler(boolean dropPongFrames) { + this.dropPongFrames = dropPongFrames; + } + @Override protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, List out) throws Exception { if (frame instanceof PingWebSocketFrame) { @@ -29,8 +50,7 @@ protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, List void assertPropagatedInbound(T message, EmbeddedChannel channel) { + T propagatedResponse = channel.readInbound(); + assertEquals(message, propagatedResponse); + } +} From 030318e53cb2ba59a746933489b8f060040a0766 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 24 May 2018 20:29:29 +0200 Subject: [PATCH 022/417] =?UTF-8?q?Read=20until=20all=20data=20is=20consum?= =?UTF-8?q?ed=20when=20EOF=20is=20detected=20even=20if=20readPend=E2=80=A6?= =?UTF-8?q?=20(#7961)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Read until all data is consumed when EOF is detected even if readPending is false and auto-read is disabled. Motivation: We should better always notify the user of EOF even if the user did not request any data as otherwise we may never be notified when the remote peer closes the connection. This should be ok as the amount of extra data we may read and so fire through the pipeline is limited by SO_RECVBUF. Modifications: - Always drain the socket when EOF is detected. - Add testcase Result: No risk for the user to be not notified of EOF. --- .../channel/epoll/AbstractEpollChannel.java | 21 +++---- .../epoll/EpollRecvByteAllocatorHandle.java | 5 +- .../channel/kqueue/AbstractKQueueChannel.java | 19 ++++--- .../kqueue/KQueueRecvByteAllocatorHandle.java | 8 ++- .../tests/DetectPeerCloseWithoutReadTest.java | 57 +++++++++++++++---- 5 files changed, 75 insertions(+), 35 deletions(-) diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java index c1af9975cd29..03574b9715cb 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java @@ -396,16 +396,9 @@ public void run() { final void epollInBefore() { maybeMoreDataToRead = false; } final void epollInFinally(ChannelConfig config) { - maybeMoreDataToRead = allocHandle.isEdgeTriggered() && allocHandle.maybeMoreDataToRead(); - // Check if there is a readPending which was not processed yet. - // This could be for two reasons: - // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method - // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method - // - // See https://github.com/netty/netty/issues/2254 - if (!readPending && !config.isAutoRead()) { - clearEpollIn(); - } else if (readPending && maybeMoreDataToRead) { + maybeMoreDataToRead = allocHandle.maybeMoreDataToRead(); + + if (allocHandle.isReceivedRdHup() || (readPending && maybeMoreDataToRead)) { // trigger a read again as there may be something left to read and because of epoll ET we // will not get notified again until we read everything from the socket // @@ -414,6 +407,14 @@ final void epollInFinally(ChannelConfig config) { // to false before every read operation to prevent re-entry into epollInReady() we will not read from // the underlying OS again unless the user happens to call read again. executeEpollInReadyRunnable(config); + } else if (!readPending && !config.isAutoRead()) { + // Check if there is a readPending which was not processed yet. + // This could be for two reasons: + // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method + // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method + // + // See https://github.com/netty/netty/issues/2254 + clearEpollIn(); } } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorHandle.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorHandle.java index 5c2c87c0c576..688cf4f7e9e3 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorHandle.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorHandle.java @@ -52,10 +52,11 @@ boolean maybeMoreDataToRead() { * respect auto read we supporting reading to stop if auto read is off. It is expected that the * {@link #EpollSocketChannel} implementations will track if we are in edgeTriggered mode and all data was not * read, and will force a EPOLLIN ready event. + * + * It is assumed RDHUP is handled externally by checking {@link #isReceivedRdHup()}. */ return (isEdgeTriggered && lastBytesRead() > 0) || - (!isEdgeTriggered && lastBytesRead() == attemptedBytesRead()) || - receivedRdHup; + (!isEdgeTriggered && lastBytesRead() == attemptedBytesRead()); } final void edgeTriggered(boolean edgeTriggered) { diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java index 698e59c7f165..7ca2bd9f1c74 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java @@ -405,15 +405,8 @@ final void readReady(long numberBytesPending) { final void readReadyFinally(ChannelConfig config) { maybeMoreDataToRead = allocHandle.maybeMoreDataToRead(); - // Check if there is a readPending which was not processed yet. - // This could be for two reasons: - // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method - // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method - // - // See https://github.com/netty/netty/issues/2254 - if (!readPending && !config.isAutoRead()) { - clearReadFilter0(); - } else if (readPending && maybeMoreDataToRead) { + + if (allocHandle.isReadEOF() || (readPending && maybeMoreDataToRead)) { // trigger a read again as there may be something left to read and because of ET we // will not get notified again until we read everything from the socket // @@ -422,6 +415,14 @@ final void readReadyFinally(ChannelConfig config) { // to false before every read operation to prevent re-entry into readReady() we will not read from // the underlying OS again unless the user happens to call read again. executeReadReadyRunnable(config); + } else if (!readPending && !config.isAutoRead()) { + // Check if there is a readPending which was not processed yet. + // This could be for two reasons: + // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method + // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method + // + // See https://github.com/netty/netty/issues/2254 + clearReadFilter0(); } } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueRecvByteAllocatorHandle.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueRecvByteAllocatorHandle.java index e192ae5560c0..087881f46142 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueRecvByteAllocatorHandle.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueRecvByteAllocatorHandle.java @@ -103,6 +103,10 @@ void readEOF() { readEOF = true; } + boolean isReadEOF() { + return readEOF; + } + void numberBytesPending(long numberBytesPending) { this.numberBytesPending = numberBytesPending; } @@ -116,9 +120,9 @@ boolean maybeMoreDataToRead() { * channel. It is expected that the {@link #KQueueSocketChannel} implementations will track if all data was not * read, and will force a EVFILT_READ ready event. * - * If EOF has been read we must read until we get an error. + * It is assumed EOF is handled externally by checking {@link #isReadEOF()}. */ - return numberBytesPending != 0 || readEOF; + return numberBytesPending != 0; } private int guess0() { diff --git a/transport-native-unix-common-tests/src/main/java/io/netty/channel/unix/tests/DetectPeerCloseWithoutReadTest.java b/transport-native-unix-common-tests/src/main/java/io/netty/channel/unix/tests/DetectPeerCloseWithoutReadTest.java index ef5482d11b01..a8b231f5e14c 100644 --- a/transport-native-unix-common-tests/src/main/java/io/netty/channel/unix/tests/DetectPeerCloseWithoutReadTest.java +++ b/transport-native-unix-common-tests/src/main/java/io/netty/channel/unix/tests/DetectPeerCloseWithoutReadTest.java @@ -25,6 +25,7 @@ import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; +import io.netty.channel.FixedRecvByteBufAllocator; import io.netty.channel.ServerChannel; import io.netty.channel.SimpleChannelInboundHandler; import org.junit.Test; @@ -41,7 +42,17 @@ public abstract class DetectPeerCloseWithoutReadTest { protected abstract Class clientChannel(); @Test(timeout = 10000) - public void clientCloseWithoutServerReadIsDetected() throws InterruptedException { + public void clientCloseWithoutServerReadIsDetectedNoExtraReadRequested() throws InterruptedException { + clientCloseWithoutServerReadIsDetected0(false); + } + + @Test(timeout = 10000) + public void clientCloseWithoutServerReadIsDetectedExtraReadRequested() throws InterruptedException { + clientCloseWithoutServerReadIsDetected0(true); + } + + private void clientCloseWithoutServerReadIsDetected0(final boolean extraReadRequested) + throws InterruptedException { EventLoopGroup serverGroup = null; EventLoopGroup clientGroup = null; Channel serverChannel = null; @@ -54,11 +65,15 @@ public void clientCloseWithoutServerReadIsDetected() throws InterruptedException ServerBootstrap sb = new ServerBootstrap(); sb.group(serverGroup); sb.channel(serverChannel()); + // Ensure we read only one message per read() call and that we need multiple read() + // calls to consume everything. sb.childOption(ChannelOption.AUTO_READ, false); + sb.childOption(ChannelOption.MAX_MESSAGES_PER_READ, 1); + sb.childOption(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(expectedBytes / 10)); sb.childHandler(new ChannelInitializer() { @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(new TestHandler(bytesRead, latch)); + protected void initChannel(Channel ch) { + ch.pipeline().addLast(new TestHandler(bytesRead, extraReadRequested, latch)); } }); @@ -89,7 +104,16 @@ protected void initChannel(Channel ch) throws Exception { } @Test(timeout = 10000) - public void serverCloseWithoutClientReadIsDetected() throws InterruptedException { + public void serverCloseWithoutClientReadIsDetectedNoExtraReadRequested() throws InterruptedException { + serverCloseWithoutClientReadIsDetected0(false); + } + + @Test(timeout = 10000) + public void serverCloseWithoutClientReadIsDetectedExtraReadRequested() throws InterruptedException { + serverCloseWithoutClientReadIsDetected0(true); + } + + private void serverCloseWithoutClientReadIsDetected0(final boolean extraReadRequested) throws InterruptedException { EventLoopGroup serverGroup = null; EventLoopGroup clientGroup = null; Channel serverChannel = null; @@ -105,10 +129,10 @@ public void serverCloseWithoutClientReadIsDetected() throws InterruptedException sb.channel(serverChannel()); sb.childHandler(new ChannelInitializer() { @Override - protected void initChannel(Channel ch) throws Exception { + protected void initChannel(Channel ch) { ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { + public void channelActive(ChannelHandlerContext ctx) { ByteBuf buf = ctx.alloc().buffer(expectedBytes); buf.writerIndex(buf.writerIndex() + expectedBytes); ctx.writeAndFlush(buf).addListener(ChannelFutureListener.CLOSE); @@ -123,11 +147,15 @@ public void channelActive(ChannelHandlerContext ctx) throws Exception { Bootstrap cb = new Bootstrap(); cb.group(serverGroup); cb.channel(clientChannel()); + // Ensure we read only one message per read() call and that we need multiple read() + // calls to consume everything. cb.option(ChannelOption.AUTO_READ, false); + cb.option(ChannelOption.MAX_MESSAGES_PER_READ, 1); + cb.option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(expectedBytes / 10)); cb.handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(new TestHandler(bytesRead, latch)); + ch.pipeline().addLast(new TestHandler(bytesRead, extraReadRequested, latch)); } }); clientChannel = cb.connect(serverChannel.localAddress()).syncUninterruptibly().channel(); @@ -152,22 +180,27 @@ protected void initChannel(Channel ch) throws Exception { private static final class TestHandler extends SimpleChannelInboundHandler { private final AtomicInteger bytesRead; + private final boolean extraReadRequested; private final CountDownLatch latch; - TestHandler(AtomicInteger bytesRead, CountDownLatch latch) { + TestHandler(AtomicInteger bytesRead, boolean extraReadRequested, CountDownLatch latch) { this.bytesRead = bytesRead; + this.extraReadRequested = extraReadRequested; this.latch = latch; } @Override - protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) { bytesRead.addAndGet(msg.readableBytes()); - // Because autoread is off, we call read to consume all data until we detect the close. - ctx.read(); + + if (extraReadRequested) { + // Because autoread is off, we call read to consume all data until we detect the close. + ctx.read(); + } } @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { + public void channelInactive(ChannelHandlerContext ctx) { latch.countDown(); ctx.fireChannelInactive(); } From c3637ff42c53e65f65bf629657638736ce102e28 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 24 May 2018 21:25:30 +0200 Subject: [PATCH 023/417] AUTO_CLOSE should not be marked as deprecated. (#7967) Motivation: A long time ago we deprecated AUTO_CLOSE but it turned out this feature is still useful because if a write error is detected there still maybe data to read, and if we close the channel automatically we will lose data Modifications: - Remove `@Deprecated` tag for AUTO_CLOSE, setAutoClose(...) and isAutoClose(...) - Fix javadocs on ChannelConfig to correctly tell the default value of AUTO_CLOSE. Result: Less warnings. --- .../src/main/java/io/netty/channel/ChannelConfig.java | 10 ++-------- .../src/main/java/io/netty/channel/ChannelOption.java | 3 --- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/ChannelConfig.java b/transport/src/main/java/io/netty/channel/ChannelConfig.java index 9e77da7a34f8..2a7f848d3605 100644 --- a/transport/src/main/java/io/netty/channel/ChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/ChannelConfig.java @@ -195,21 +195,15 @@ public interface ChannelConfig { ChannelConfig setAutoRead(boolean autoRead); /** - * @deprecated Auto close will be removed in a future release. - * * Returns {@code true} if and only if the {@link Channel} will be closed automatically and immediately on - * write failure. The default is {@code false}. + * write failure. The default is {@code true}. */ - @Deprecated boolean isAutoClose(); /** - * @deprecated Auto close will be removed in a future release. - * * Sets whether the {@link Channel} should be closed automatically and immediately on write failure. - * The default is {@code false}. + * The default is {@code true}. */ - @Deprecated ChannelConfig setAutoClose(boolean autoClose); /** diff --git a/transport/src/main/java/io/netty/channel/ChannelOption.java b/transport/src/main/java/io/netty/channel/ChannelOption.java index 4cb909a1db1a..d026a30a5db3 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOption.java +++ b/transport/src/main/java/io/netty/channel/ChannelOption.java @@ -99,12 +99,9 @@ public static ChannelOption newInstance(String name) { public static final ChannelOption AUTO_READ = valueOf("AUTO_READ"); /** - * @deprecated Auto close will be removed in a future release. - * * If {@code true} then the {@link Channel} is closed automatically and immediately on write failure. * The default value is {@code true}. */ - @Deprecated public static final ChannelOption AUTO_CLOSE = valueOf("AUTO_CLOSE"); public static final ChannelOption SO_BROADCAST = valueOf("SO_BROADCAST"); From 8a85761500238893ca35bfa42a3601ef15447018 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 25 May 2018 14:35:32 +0200 Subject: [PATCH 024/417] Don't use VM.maxDirectMemory() on IBM J9 / Eclipse OpenJ9 to retrieve direct memory limit (#7966) Motivation: On J9 / OpenJ9 netty initializes this value with 64M, even the direct accessible memory is actually unbounded. Modifications: Skip usage of VM.maxDirectMemory() on J9 / OpenJ9 Result: More correct direct memory limit detection. Fixes #7654. --- .../java/io/netty/util/internal/PlatformDependent.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index 4623459c5dd8..e6b082d859bf 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -993,10 +993,14 @@ private static long maxDirectMemory0() { try { systemClassLoader = getSystemClassLoader(); - // On z/OS we should not use VM.maxDirectMemory() as it not reflects the correct value. + // When using IBM J9 / Eclipse OpenJ9 we should not use VM.maxDirectMemory() as it not reflects the + // correct value. // See: // - https://github.com/netty/netty/issues/7654 - if (!SystemPropertyUtil.get("os.name", "").toLowerCase().contains("z/os")) { + String vmName = SystemPropertyUtil.get("java.vm.name", "").toLowerCase(); + if (!vmName.startsWith("ibm j9") && + // https://github.com/eclipse/openj9/blob/openj9-0.8.0/runtime/include/vendor_version.h#L53 + !vmName.startsWith("eclipse openj9")) { // Try to get from sun.misc.VM.maxDirectMemory() which should be most accurate. Class vmClass = Class.forName("sun.misc.VM", true, systemClassLoader); Method m = vmClass.getDeclaredMethod("maxDirectMemory"); From 9a3311506e5aa9ffd4bfa58c47ec22c37a0d5599 Mon Sep 17 00:00:00 2001 From: Nick Travers Date: Sun, 27 May 2018 01:02:49 -0700 Subject: [PATCH 025/417] Run the WebSocketClientHandshaker08Test on the 08 implementation (#7974) Motivation: The websockets abstract test suite does not run against the 08 implementation in the 08 version of the test suite. Modifications: Update the WebSocketClientHandshaker08Test to instantiate a new WebSocketClientHandshaker08 rather than an 07 handshaker. Result: The WebSocketClientHandshaker08Test now tests the 08 implementation. --- .../codec/http/websocketx/WebSocketClientHandshaker08Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08Test.java index 4ce8016adda3..0af3030f3319 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08Test.java @@ -20,6 +20,6 @@ public class WebSocketClientHandshaker08Test extends WebSocketClientHandshaker07Test { @Override protected WebSocketClientHandshaker newHandshaker(URI uri) { - return new WebSocketClientHandshaker07(uri, WebSocketVersion.V08, null, false, null, 1024); + return new WebSocketClientHandshaker08(uri, WebSocketVersion.V08, null, false, null, 1024); } } From 94946f63247b9b9b02294f49cbcd8868a0c7609f Mon Sep 17 00:00:00 2001 From: tomer doron Date: Mon, 28 May 2018 01:31:48 -0700 Subject: [PATCH 026/417] add docker-compose to allow easy testing across linux and java versions (#7958) motivation: setup for testing across different permutations of linux and java versions changes: * refactor docker file to allow dynamic versions of centos and java * add docker compose driver files for centos 6, 7 and java 1.8, 1.9, 1.10, 1.11 * update instructions --- docker/Dockerfile-netty-centos6 | 34 ------------------------- docker/Dockerfile.centos | 27 ++++++++++++++++++++ docker/README.md | 14 ++++++---- docker/docker-compose.centos-6.110.yaml | 13 ++++++++++ docker/docker-compose.centos-6.111.yaml | 13 ++++++++++ docker/docker-compose.centos-6.18.yaml | 13 ++++++++++ docker/docker-compose.centos-6.19.yaml | 13 ++++++++++ docker/docker-compose.centos-7.110.yaml | 13 ++++++++++ docker/docker-compose.centos-7.111.yaml | 13 ++++++++++ docker/docker-compose.centos-7.18.yaml | 13 ++++++++++ docker/docker-compose.centos-7.19.yaml | 13 ++++++++++ docker/docker-compose.yaml | 23 +++++++++++++++++ 12 files changed, 163 insertions(+), 39 deletions(-) delete mode 100644 docker/Dockerfile-netty-centos6 create mode 100644 docker/Dockerfile.centos create mode 100644 docker/docker-compose.centos-6.110.yaml create mode 100644 docker/docker-compose.centos-6.111.yaml create mode 100644 docker/docker-compose.centos-6.18.yaml create mode 100644 docker/docker-compose.centos-6.19.yaml create mode 100644 docker/docker-compose.centos-7.110.yaml create mode 100644 docker/docker-compose.centos-7.111.yaml create mode 100644 docker/docker-compose.centos-7.18.yaml create mode 100644 docker/docker-compose.centos-7.19.yaml create mode 100644 docker/docker-compose.yaml diff --git a/docker/Dockerfile-netty-centos6 b/docker/Dockerfile-netty-centos6 deleted file mode 100644 index d6e2339f5105..000000000000 --- a/docker/Dockerfile-netty-centos6 +++ /dev/null @@ -1,34 +0,0 @@ -FROM centos:6 -MAINTAINER netty@googlegroups.com -ENTRYPOINT /bin/bash - -ENV SOURCE_DIR $HOME/source -ENV MAVEN_VERSION 3.5.2 -ENV JAVA_VERSION 1.8.0 - -RUN mkdir $SOURCE_DIR -WORKDIR $SOURCE_DIR - -# install dependencies -RUN yum install -y \ - apr-devel \ - autoconf \ - automake \ - git \ - glibc-devel \ - java-$JAVA_VERSION-openjdk-devel \ - libtool \ - lksctp-tools \ - lsb-core \ - make \ - openssl-devel \ - tar \ - wget - - -RUN wget -q http://archive.apache.org/dist/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz && tar xfz apache-maven-$MAVEN_VERSION-bin.tar.gz && mv apache-maven-$MAVEN_VERSION /opt/ - -RUN echo 'PATH=/opt/apache-maven-$MAVEN_VERSION/bin:$PATH' >> ~/.bashrc -RUN echo 'export JAVA_HOME="/usr/lib/jvm/java-$JAVA_VERSION/"' >> ~/.bashrc - -RUN rm -rf $SOURCE_DIR diff --git a/docker/Dockerfile.centos b/docker/Dockerfile.centos new file mode 100644 index 000000000000..4d888209023f --- /dev/null +++ b/docker/Dockerfile.centos @@ -0,0 +1,27 @@ +ARG centos_version=6 +FROM centos:$centos_version +# needed to do again after FROM due to docker limitation +ARG centos_version + +# install dependencies +RUN yum install -y \ + apr-devel \ + autoconf \ + automake \ + git \ + glibc-devel \ + libtool \ + lksctp-tools \ + lsb-core \ + make \ + openssl-devel \ + tar \ + wget + +ARG java_version=1.8 +ENV JAVA_VERSION $java_version +# installing java with jabba +RUN curl -sL https://github.com/shyiko/jabba/raw/master/install.sh | JABBA_COMMAND="install $JAVA_VERSION -o /jdk" bash + +RUN echo 'export JAVA_HOME="/jdk"' >> ~/.bashrc +RUN echo 'PATH=/jdk/bin:$PATH' >> ~/.bashrc diff --git a/docker/README.md b/docker/README.md index 157236f5bbad..9970aaed5da8 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,15 +1,19 @@ +# Using the docker images -** Create a docker image ** ``` -docker build -f Dockerfile-netty-centos6 . -t netty-centos6 +cd /path/to/netty/ ``` -** Using the image ** +## centos 6 with java 8 ``` -cd /path/to/netty/ +docker-compose -f docker/docker-compose.yaml -f docker/docker-compose.centos-6.18.yaml run test ``` +## centos 7 with java 9 + ``` -docker run -it -v ~/.m2:/root/.m2 -v ~/.ssh:/root/.ssh -v ~/.gnupg:/root/.gnupg -v `pwd`:/code -w /code netty-centos6 bash +docker-compose -f docker/docker-compose.yaml -f docker/docker-compose.centos-7.19.yaml run test ``` + +etc, etc diff --git a/docker/docker-compose.centos-6.110.yaml b/docker/docker-compose.centos-6.110.yaml new file mode 100644 index 000000000000..8b2674b4ab25 --- /dev/null +++ b/docker/docker-compose.centos-6.110.yaml @@ -0,0 +1,13 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-6-1.10 + build: + args: + centos_version : "6" + java_version : "1.10-0" + + test: + image: netty:centos-6-1.10 diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml new file mode 100644 index 000000000000..ee178141e4cf --- /dev/null +++ b/docker/docker-compose.centos-6.111.yaml @@ -0,0 +1,13 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-6-1.11 + build: + args: + centos_version : "6" + java_version : "1.11.0-14" + + test: + image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-6.18.yaml b/docker/docker-compose.centos-6.18.yaml new file mode 100644 index 000000000000..88f433a8d4a0 --- /dev/null +++ b/docker/docker-compose.centos-6.18.yaml @@ -0,0 +1,13 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-6-1.8 + build: + args: + centos_version : "6" + java_version : "1.8" + + test: + image: netty:centos-6-1.8 diff --git a/docker/docker-compose.centos-6.19.yaml b/docker/docker-compose.centos-6.19.yaml new file mode 100644 index 000000000000..8a5e7bbe27d5 --- /dev/null +++ b/docker/docker-compose.centos-6.19.yaml @@ -0,0 +1,13 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-6-1.9 + build: + args: + centos_version : "6" + java_version : "zulu@1.9.0" + + test: + image: netty:centos-6-1.9 diff --git a/docker/docker-compose.centos-7.110.yaml b/docker/docker-compose.centos-7.110.yaml new file mode 100644 index 000000000000..553720c30f0f --- /dev/null +++ b/docker/docker-compose.centos-7.110.yaml @@ -0,0 +1,13 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-7-1.10 + build: + args: + centos_version : "7" + java_version : "1.10-0" + + test: + image: netty:centos-7-1.10 diff --git a/docker/docker-compose.centos-7.111.yaml b/docker/docker-compose.centos-7.111.yaml new file mode 100644 index 000000000000..903db9fdab90 --- /dev/null +++ b/docker/docker-compose.centos-7.111.yaml @@ -0,0 +1,13 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-7-1.11 + build: + args: + centos_version : "7" + java_version : "1.11.0-14" + + test: + image: netty:centos-7-1.11 diff --git a/docker/docker-compose.centos-7.18.yaml b/docker/docker-compose.centos-7.18.yaml new file mode 100644 index 000000000000..d592aeada9ff --- /dev/null +++ b/docker/docker-compose.centos-7.18.yaml @@ -0,0 +1,13 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-7-1.8 + build: + args: + centos_version : "7" + java_version : "1.8" + + test: + image: netty:centos-7-1.8 diff --git a/docker/docker-compose.centos-7.19.yaml b/docker/docker-compose.centos-7.19.yaml new file mode 100644 index 000000000000..186669ddb4aa --- /dev/null +++ b/docker/docker-compose.centos-7.19.yaml @@ -0,0 +1,13 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-7-1.9 + build: + args: + centos_version : "7" + java_version : "zulu@1.9.0" + + test: + image: netty:centos-7-1.9 diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml new file mode 100644 index 000000000000..ec11d507275c --- /dev/null +++ b/docker/docker-compose.yaml @@ -0,0 +1,23 @@ +version: "3" + +services: + + runtime-setup: + image: netty:default + build: + context: . + dockerfile: Dockerfile.centos + + common: &common + image: netty:default + depends_on: [runtime-setup] + volumes: + - ~/.ssh:/root/.ssh + - ~/.m2:/root/.m2 + - ~/.gnupg:/root/.gnupg + - ..:/code + working_dir: /code + + test: + <<: *common + command: /bin/bash -cl "./mvnw clean package -Dio.netty.testsuite.badHost=netty.io -DskipOsgiTestsuite=true" From 7f59896fba28ca1b14e13d2c7b3ba60f7af74027 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 28 May 2018 11:51:25 +0200 Subject: [PATCH 027/417] Use jdk-11-ea+15 when try to build with java11 (#7979) Motivation: A new EA build for java 11 is out. Modifications: Update from ea+14 to ea+15 Result: Use latest ea build --- docker/docker-compose.centos-6.111.yaml | 2 +- docker/docker-compose.centos-7.111.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml index ee178141e4cf..b271c5fcdcb6 100644 --- a/docker/docker-compose.centos-6.111.yaml +++ b/docker/docker-compose.centos-6.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "1.11.0-14" + java_version : "1.11.0-15" test: image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-7.111.yaml b/docker/docker-compose.centos-7.111.yaml index 903db9fdab90..9ba2ca830d53 100644 --- a/docker/docker-compose.centos-7.111.yaml +++ b/docker/docker-compose.centos-7.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "1.11.0-14" + java_version : "1.11.0-15" test: image: netty:centos-7-1.11 From f904c63a535ebfd51d6911cca28aac0eb23c8df0 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 29 May 2018 07:27:40 +0200 Subject: [PATCH 028/417] Correctly let Http2UnkownFrame extend HttpStreamFrame and so be usable with Http2MultiplexCodec. (#7976) Motivation: This is a followup for #7860. In the fix for #7860 we only partly fixed the problem as Http2UnknownFrame did not correctly extend HttpStreamFrame and so only worked when using the Http2FrameCodec. We need to have it extend HttpStreamFrame as otherwise Http2MultiplexCodec will reject to handle it correctly. Modifications: - Let Http2UnknownFrame extend HttpStreamFrame - Add unit tests for writing and reading Http2UnkownFrame instances when the Http2MultiplexCodec is used. Result: Fixes https://github.com/netty/netty/issues/7969. --- .../codec/http2/Http2UnknownFrame.java | 7 ++-- .../codec/http2/Http2MultiplexCodecTest.java | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2UnknownFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2UnknownFrame.java index 51eeafaf0d95..41e578703a5b 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2UnknownFrame.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2UnknownFrame.java @@ -20,13 +20,12 @@ import io.netty.util.internal.UnstableApi; @UnstableApi -public interface Http2UnknownFrame extends Http2Frame, ByteBufHolder { +public interface Http2UnknownFrame extends Http2StreamFrame, ByteBufHolder { + @Override Http2FrameStream stream(); - /** - * Set the {@link Http2FrameStream} object for this frame. - */ + @Override Http2UnknownFrame stream(Http2FrameStream stream); byte frameType(); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java index 7a0e8c6a3a06..f07b218e0950 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java @@ -104,6 +104,43 @@ public void tearDown() throws Exception { // TODO(buchgr): GOAWAY Logic // TODO(buchgr): Test ChannelConfig.setMaxMessagesPerRead + @Test + public void writeUnknownFrame() { + childChannelInitializer.handler = new ChannelInboundHandlerAdapter() { + @Override + public void channelActive(ChannelHandlerContext ctx) { + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); + ctx.writeAndFlush(new DefaultHttp2UnknownFrame((byte) 99, new Http2Flags())); + ctx.fireChannelActive(); + } + }; + + Channel childChannel = newOutboundStream(); + assertTrue(childChannel.isActive()); + + Http2FrameStream stream = readOutboundHeadersAndAssignId(); + parentChannel.runPendingTasks(); + + Http2UnknownFrame frame = parentChannel.readOutbound(); + assertEquals(stream, frame.stream()); + assertEquals(99, frame.frameType()); + assertEquals(new Http2Flags(), frame.flags()); + frame.release(); + } + + @Test + public void readUnkownFrame() { + LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); + codec.onHttp2Frame(new DefaultHttp2UnknownFrame((byte) 99, new Http2Flags()).stream(inboundStream)); + codec.onChannelReadComplete(); + + // headers and unknown frame + verifyFramesMultiplexedToCorrectChannel(inboundStream, inboundHandler, 2); + + Channel childChannel = newOutboundStream(); + assertTrue(childChannel.isActive()); + } + @Test public void headerAndDataFramesShouldBeDelivered() { LastInboundHandler inboundHandler = new LastInboundHandler(); From 2e587f75cbb8125e02f3622a7ec6d9cd5e3dd47c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 29 May 2018 10:59:42 +0200 Subject: [PATCH 029/417] Don't trim stacktrace for exceptions when running mvn test (#7981) Motivation: The maven surefire plugin will trim stacktraces by default which makes these kind of use-less when trying to understand why an test failed because one was thrown. Modifications: Configure the plugin to not trim the stacktrace. Result: Easier to debug test-failures. --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index caf9cb5f312c..4cb89d220c50 100644 --- a/pom.xml +++ b/pom.xml @@ -864,6 +864,8 @@ ${testJavaHome}/bin/java + + false From 0c6f077c18d2a721b94a4fe89bc44157cb1b6a66 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 29 May 2018 11:09:22 +0200 Subject: [PATCH 030/417] Allow to test with boringssl-static and drop into shell (#7980) Motivation: We also need to run our tests while using boringssl-static to ensure everything works when using it. Beside this its sometimes useful to be able to just get a shell and so interactive work in the docker instance. Modifications: - Add configs for shell - Add configs for testing with boringssl-static - Ensure we not share .m2 when running tests Result: More complete docker setup. --- docker/docker-compose.centos-6.110.yaml | 6 ++++++ docker/docker-compose.centos-6.111.yaml | 6 ++++++ docker/docker-compose.centos-6.18.yaml | 6 ++++++ docker/docker-compose.centos-6.19.yaml | 6 ++++++ docker/docker-compose.centos-7.110.yaml | 6 ++++++ docker/docker-compose.centos-7.111.yaml | 6 ++++++ docker/docker-compose.centos-7.18.yaml | 6 ++++++ docker/docker-compose.centos-7.19.yaml | 6 ++++++ docker/docker-compose.yaml | 16 ++++++++++++++-- 9 files changed, 62 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.110.yaml b/docker/docker-compose.centos-6.110.yaml index 8b2674b4ab25..1d96010f447b 100644 --- a/docker/docker-compose.centos-6.110.yaml +++ b/docker/docker-compose.centos-6.110.yaml @@ -11,3 +11,9 @@ services: test: image: netty:centos-6-1.10 + + test-boringssl-static: + image: netty:centos-6-1.10 + + shell: + image: netty:centos-6-1.10 diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml index b271c5fcdcb6..c5a8103a1e75 100644 --- a/docker/docker-compose.centos-6.111.yaml +++ b/docker/docker-compose.centos-6.111.yaml @@ -11,3 +11,9 @@ services: test: image: netty:centos-6-1.11 + + test-boringssl-static: + image: netty:centos-6-1.11 + + shell: + image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-6.18.yaml b/docker/docker-compose.centos-6.18.yaml index 88f433a8d4a0..986e3fb2be80 100644 --- a/docker/docker-compose.centos-6.18.yaml +++ b/docker/docker-compose.centos-6.18.yaml @@ -11,3 +11,9 @@ services: test: image: netty:centos-6-1.8 + + test-boringssl-static: + image: netty:centos-6-1.8 + + shell: + image: netty:centos-6-1.8 diff --git a/docker/docker-compose.centos-6.19.yaml b/docker/docker-compose.centos-6.19.yaml index 8a5e7bbe27d5..54f32b9ef5eb 100644 --- a/docker/docker-compose.centos-6.19.yaml +++ b/docker/docker-compose.centos-6.19.yaml @@ -11,3 +11,9 @@ services: test: image: netty:centos-6-1.9 + + test-boringssl-static: + image: netty:centos-6-1.9 + + shell: + image: netty:centos-6-1.9 diff --git a/docker/docker-compose.centos-7.110.yaml b/docker/docker-compose.centos-7.110.yaml index 553720c30f0f..d823f4bcbb52 100644 --- a/docker/docker-compose.centos-7.110.yaml +++ b/docker/docker-compose.centos-7.110.yaml @@ -11,3 +11,9 @@ services: test: image: netty:centos-7-1.10 + + test-boringssl-static: + image: netty:centos-7-1.10 + + shell: + image: netty:centos-7-1.10 diff --git a/docker/docker-compose.centos-7.111.yaml b/docker/docker-compose.centos-7.111.yaml index 9ba2ca830d53..ee069d99bf5f 100644 --- a/docker/docker-compose.centos-7.111.yaml +++ b/docker/docker-compose.centos-7.111.yaml @@ -11,3 +11,9 @@ services: test: image: netty:centos-7-1.11 + + test-boringssl-static: + image: netty:centos-7-1.11 + + shell: + image: netty:centos-7-1.11 diff --git a/docker/docker-compose.centos-7.18.yaml b/docker/docker-compose.centos-7.18.yaml index d592aeada9ff..31c8d5127dd6 100644 --- a/docker/docker-compose.centos-7.18.yaml +++ b/docker/docker-compose.centos-7.18.yaml @@ -11,3 +11,9 @@ services: test: image: netty:centos-7-1.8 + + test-boringssl-static: + image: netty:centos-7-1.8 + + shell: + image: netty:centos-7-1.8 diff --git a/docker/docker-compose.centos-7.19.yaml b/docker/docker-compose.centos-7.19.yaml index 186669ddb4aa..3f41d6354d54 100644 --- a/docker/docker-compose.centos-7.19.yaml +++ b/docker/docker-compose.centos-7.19.yaml @@ -11,3 +11,9 @@ services: test: image: netty:centos-7-1.9 + + test-boringssl-static: + image: netty:centos-7-1.9 + + shell: + image: netty:centos-7-1.9 diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index ec11d507275c..834f6ce1d98a 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -13,11 +13,23 @@ services: depends_on: [runtime-setup] volumes: - ~/.ssh:/root/.ssh - - ~/.m2:/root/.m2 - ~/.gnupg:/root/.gnupg - ..:/code working_dir: /code test: <<: *common - command: /bin/bash -cl "./mvnw clean package -Dio.netty.testsuite.badHost=netty.io -DskipOsgiTestsuite=true" + command: /bin/bash -cl "./mvnw clean install -Dio.netty.testsuite.badHost=netty.io" + + test-boringssl-static: + <<: *common + command: /bin/bash -cl "./mvnw clean install -Dio.netty.testsuite.badHost=netty.io -Dtcnative.artifactId=netty-tcnative-boringssl-static" + + shell: + <<: *common + volumes: + - ~/.ssh:/root/.ssh + - ~/.gnupg:/root/.gnupg + - ..:/code + - ~/.m2:/root/.m2 + entrypoint: /bin/bash From 4b728cd5bc53195bced516f33a1ea0a0def5604e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 29 May 2018 19:48:40 +0200 Subject: [PATCH 031/417] Ignore some test-flakiness when using Java11+ due outstanding Java11 bug. (#7984) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: Java11 disallow draining any remaining bytes from the socket if a write causes a connection reset. This should be completely safe to do. At the moment if a write is causing a connection-reset you basically loose all the pending bytes that are sitting on the socket and are waiting to be read. This happens because SocketOutputStream.write(…) may call AbstractPlainSocketImpl.setConnectionReset(…). Once this method is called any read(…) call will just throw a SocketException without even attempt to read any remaining data. This is related: - https://bugs.openjdk.java.net/browse/JDK-8199329 - http://hg.openjdk.java.net/jdk/jdk/rev/92cca24c8807 - http://mail.openjdk.java.net/pipermail/net-dev/2018-May/011511.html Modifications: Tolarate if remaining bytes could not be read when using OIO. Result: Be able to build Netty and run testsuite while using Java11 --- .../socket/SocketHalfClosedTest.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java index cd03ac3c9be7..3576414e0911 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java @@ -33,7 +33,9 @@ import io.netty.channel.socket.ChannelInputShutdownReadComplete; import io.netty.channel.socket.ChannelOutputShutdownEvent; import io.netty.channel.socket.DuplexChannel; +import io.netty.channel.socket.oio.OioSocketChannel; import io.netty.util.UncheckedBooleanSupplier; +import io.netty.util.internal.PlatformDependent; import org.junit.Test; import java.util.concurrent.CountDownLatch; @@ -414,19 +416,26 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { @Override public void channelInactive(ChannelHandlerContext ctx) { - checkPrematureClose(); + checkPrematureClose(ctx); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { ctx.close(); - checkPrematureClose(); + checkPrematureClose(ctx); } - private void checkPrematureClose() { + private void checkPrematureClose(ChannelHandlerContext ctx) { if (bytesRead < expectedBytes || !seenOutputShutdown) { - causeRef.set(new IllegalStateException("leader premature close")); - doneLatch.countDown(); + if (ctx.channel() instanceof OioSocketChannel && seenOutputShutdown + && PlatformDependent.javaVersion() >= 11) { + // If we are using OIO and are using Java11 this is expected atm. + // See http://mail.openjdk.java.net/pipermail/net-dev/2018-May/011511.html. + doneLatch.countDown(); + } else { + causeRef.set(new IllegalStateException("leader premature close")); + doneLatch.countDown(); + } } } } From ec91c40bf755270795d9487fcc7c276e984139f4 Mon Sep 17 00:00:00 2001 From: pifuant Date: Wed, 30 May 2018 15:09:18 +0800 Subject: [PATCH 032/417] We should re-use the same handler instance as its sharable in the example Motivation: We should re-use the same handler instance as its sharable. Modification: Re-use instance Result: More correct example --- example/src/main/java/io/netty/example/echo/EchoServer.java | 3 ++- .../src/main/java/io/netty/example/sctp/SctpEchoServer.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/example/src/main/java/io/netty/example/echo/EchoServer.java b/example/src/main/java/io/netty/example/echo/EchoServer.java index ddc43fe042c9..b7b4a770595c 100644 --- a/example/src/main/java/io/netty/example/echo/EchoServer.java +++ b/example/src/main/java/io/netty/example/echo/EchoServer.java @@ -51,6 +51,7 @@ public static void main(String[] args) throws Exception { // Configure the server. EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); + final EchoServerHandler serverHandler = new EchoServerHandler(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) @@ -65,7 +66,7 @@ public void initChannel(SocketChannel ch) throws Exception { p.addLast(sslCtx.newHandler(ch.alloc())); } //p.addLast(new LoggingHandler(LogLevel.INFO)); - p.addLast(new EchoServerHandler()); + p.addLast(serverHandler); } }); diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoServer.java b/example/src/main/java/io/netty/example/sctp/SctpEchoServer.java index 96493f4ddfa4..4346e399bdeb 100644 --- a/example/src/main/java/io/netty/example/sctp/SctpEchoServer.java +++ b/example/src/main/java/io/netty/example/sctp/SctpEchoServer.java @@ -37,6 +37,7 @@ public static void main(String[] args) throws Exception { // Configure the server. EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); + final SctpEchoServerHandler serverHandler = new SctpEchoServerHandler(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) @@ -48,7 +49,7 @@ public static void main(String[] args) throws Exception { public void initChannel(SctpChannel ch) throws Exception { ch.pipeline().addLast( //new LoggingHandler(LogLevel.INFO), - new SctpEchoServerHandler()); + serverHandler); } }); From d133bf06a487cfa7378725dcff92e39e10633e8a Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 30 May 2018 11:11:42 +0200 Subject: [PATCH 033/417] Allow to schedule tasks up to Long.MAX_VALUE (#7972) Motivation: We should allow to schedule tasks with a delay up to Long.MAX_VALUE as we did pre 4.1.25.Final. Modifications: Just ensure we not overflow and put the correct max limits in place when schedule a timer. At worse we will get a wakeup to early and then schedule a new timeout. Result: Fixes https://github.com/netty/netty/issues/7970. --- .../AbstractScheduledEventExecutor.java | 20 +++++++++----- .../util/concurrent/ScheduledFutureTask.java | 4 ++- .../concurrent/ScheduledFutureTaskTest.java | 27 +++++++++++++++++++ .../netty/channel/epoll/EpollEventLoop.java | 13 ++------- .../channel/epoll/EpollEventLoopTest.java | 25 ++--------------- .../netty/channel/kqueue/KQueueEventLoop.java | 11 -------- .../channel/kqueue/KQueueEventLoopTest.java | 25 ++--------------- .../io/netty/channel/nio/NioEventLoop.java | 10 ------- .../netty/channel/nio/NioEventLoopTest.java | 23 ++-------------- 9 files changed, 52 insertions(+), 106 deletions(-) create mode 100644 common/src/test/java/io/netty/util/concurrent/ScheduledFutureTaskTest.java diff --git a/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java index 795aeba907c0..6585cd84f548 100644 --- a/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java +++ b/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java @@ -150,7 +150,7 @@ public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) if (delay < 0) { delay = 0; } - validateScheduled(delay, unit); + validateScheduled0(delay, unit); return schedule(new ScheduledFutureTask( this, command, null, ScheduledFutureTask.deadlineNanos(unit.toNanos(delay)))); @@ -163,7 +163,7 @@ public ScheduledFuture schedule(Callable callable, long delay, TimeUni if (delay < 0) { delay = 0; } - validateScheduled(delay, unit); + validateScheduled0(delay, unit); return schedule(new ScheduledFutureTask( this, callable, ScheduledFutureTask.deadlineNanos(unit.toNanos(delay)))); @@ -181,8 +181,8 @@ public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDela throw new IllegalArgumentException( String.format("period: %d (expected: > 0)", period)); } - validateScheduled(initialDelay, unit); - validateScheduled(period, unit); + validateScheduled0(initialDelay, unit); + validateScheduled0(period, unit); return schedule(new ScheduledFutureTask( this, Executors.callable(command, null), @@ -202,17 +202,25 @@ public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialD String.format("delay: %d (expected: > 0)", delay)); } - validateScheduled(initialDelay, unit); - validateScheduled(delay, unit); + validateScheduled0(initialDelay, unit); + validateScheduled0(delay, unit); return schedule(new ScheduledFutureTask( this, Executors.callable(command, null), ScheduledFutureTask.deadlineNanos(unit.toNanos(initialDelay)), -unit.toNanos(delay))); } + @SuppressWarnings("deprecation") + private void validateScheduled0(long amount, TimeUnit unit) { + validateScheduled(amount, unit); + } + /** * Sub-classes may override this to restrict the maximal amount of time someone can use to schedule a task. + * + * @deprecated will be removed in the future. */ + @Deprecated protected void validateScheduled(long amount, TimeUnit unit) { // NOOP } diff --git a/common/src/main/java/io/netty/util/concurrent/ScheduledFutureTask.java b/common/src/main/java/io/netty/util/concurrent/ScheduledFutureTask.java index 6043d6ffec0f..1eaa7b927625 100644 --- a/common/src/main/java/io/netty/util/concurrent/ScheduledFutureTask.java +++ b/common/src/main/java/io/netty/util/concurrent/ScheduledFutureTask.java @@ -35,7 +35,9 @@ static long nanoTime() { } static long deadlineNanos(long delay) { - return nanoTime() + delay; + long deadlineNanos = nanoTime() + delay; + // Guard against overflow + return deadlineNanos < 0 ? Long.MAX_VALUE : deadlineNanos; } private final long id = nextTaskId.getAndIncrement(); diff --git a/common/src/test/java/io/netty/util/concurrent/ScheduledFutureTaskTest.java b/common/src/test/java/io/netty/util/concurrent/ScheduledFutureTaskTest.java new file mode 100644 index 000000000000..1c12692ff312 --- /dev/null +++ b/common/src/test/java/io/netty/util/concurrent/ScheduledFutureTaskTest.java @@ -0,0 +1,27 @@ +/* + * Copyright 2018 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: + * + * http://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 org.junit.Assert; +import org.junit.Test; + +public class ScheduledFutureTaskTest { + + @Test + public void testDeadlineNanosNotOverflow() { + Assert.assertEquals(Long.MAX_VALUE, ScheduledFutureTask.deadlineNanos(Long.MAX_VALUE)); + } +} diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index cf55ef67ec55..913f5edf4291 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -37,7 +37,6 @@ import java.util.Queue; import java.util.concurrent.Callable; import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import static java.lang.Math.min; @@ -80,7 +79,7 @@ public Integer call() throws Exception { private volatile int ioRatio = 50; // See http://man7.org/linux/man-pages/man2/timerfd_create.2.html. - static final long MAX_SCHEDULED_DAYS = TimeUnit.SECONDS.toDays(999999999); + private static final long MAX_SCHEDULED_TIMERFD_NS = 999999999; EpollEventLoop(EventLoopGroup parent, Executor executor, int maxEvents, SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) { @@ -237,7 +236,7 @@ private int epollWait(boolean oldWakeup) throws IOException { long totalDelay = delayNanos(System.nanoTime()); int delaySeconds = (int) min(totalDelay / 1000000000L, Integer.MAX_VALUE); return Native.epollWait(epollFd, events, timerFd, delaySeconds, - (int) min(totalDelay - delaySeconds * 1000000000L, Integer.MAX_VALUE)); + (int) min(MAX_SCHEDULED_TIMERFD_NS, totalDelay - delaySeconds * 1000000000L)); } private int epollWaitNow() throws IOException { @@ -453,12 +452,4 @@ protected void cleanup() { events.free(); } } - - @Override - protected void validateScheduled(long amount, TimeUnit unit) { - long days = unit.toDays(amount); - if (days > MAX_SCHEDULED_DAYS) { - throw new IllegalArgumentException("days: " + days + " (expected: < " + MAX_SCHEDULED_DAYS + ')'); - } - } } diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollEventLoopTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollEventLoopTest.java index 0fe824b334d4..ebf529f73283 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollEventLoopTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollEventLoopTest.java @@ -24,32 +24,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; public class EpollEventLoopTest { - @Test(timeout = 5000L) - public void testScheduleBigDelayOverMax() { - EventLoopGroup group = new EpollEventLoopGroup(1); - - final EventLoop el = group.next(); - try { - el.schedule(new Runnable() { - @Override - public void run() { - // NOOP - } - }, Integer.MAX_VALUE, TimeUnit.DAYS); - fail(); - } catch (IllegalArgumentException expected) { - // expected - } - - group.shutdownGracefully(); - } - @Test - public void testScheduleBigDelay() { + public void testScheduleBigDelayNotOverflow() { EventLoopGroup group = new EpollEventLoopGroup(1); final EventLoop el = group.next(); @@ -58,7 +37,7 @@ public void testScheduleBigDelay() { public void run() { // NOOP } - }, EpollEventLoop.MAX_SCHEDULED_DAYS, TimeUnit.DAYS); + }, Long.MAX_VALUE, TimeUnit.MILLISECONDS); assertFalse(future.awaitUninterruptibly(1000)); assertTrue(future.cancel(true)); diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java index 3badcea96c75..5af59ba912c6 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java @@ -33,7 +33,6 @@ import java.util.Queue; import java.util.concurrent.Callable; import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import static io.netty.channel.kqueue.KQueueEventArray.deleteGlobalRefs; @@ -77,8 +76,6 @@ public Integer call() throws Exception { private volatile int wakenUp; private volatile int ioRatio = 50; - static final long MAX_SCHEDULED_DAYS = 365 * 3; - KQueueEventLoop(EventLoopGroup parent, Executor executor, int maxEvents, SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) { super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler); @@ -370,12 +367,4 @@ private static void handleLoopException(Throwable t) { // Ignore. } } - - @Override - protected void validateScheduled(long amount, TimeUnit unit) { - long days = unit.toDays(amount); - if (days > MAX_SCHEDULED_DAYS) { - throw new IllegalArgumentException("days: " + days + " (expected: < " + MAX_SCHEDULED_DAYS + ')'); - } - } } diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueEventLoopTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueEventLoopTest.java index c7ad56e37ab1..0d441559994a 100644 --- a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueEventLoopTest.java +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueEventLoopTest.java @@ -24,32 +24,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; public class KQueueEventLoopTest { - @Test(timeout = 5000L) - public void testScheduleBigDelayOverMax() { - EventLoopGroup group = new KQueueEventLoopGroup(1); - - final EventLoop el = group.next(); - try { - el.schedule(new Runnable() { - @Override - public void run() { - // NOOP - } - }, Integer.MAX_VALUE, TimeUnit.DAYS); - fail(); - } catch (IllegalArgumentException expected) { - // expected - } - - group.shutdownGracefully(); - } - @Test - public void testScheduleBigDelay() { + public void testScheduleBigDelayNotOverflow() { EventLoopGroup group = new KQueueEventLoopGroup(1); final EventLoop el = group.next(); @@ -58,7 +37,7 @@ public void testScheduleBigDelay() { public void run() { // NOOP } - }, KQueueEventLoop.MAX_SCHEDULED_DAYS, TimeUnit.DAYS); + }, Long.MAX_VALUE, TimeUnit.MILLISECONDS); assertFalse(future.awaitUninterruptibly(1000)); assertTrue(future.cancel(true)); diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index b1e96e74973c..d71e3f955dd7 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -113,8 +113,6 @@ public Void run() { } } - static final long MAX_SCHEDULED_DAYS = 365 * 3; - /** * The NIO {@link Selector}. */ @@ -825,12 +823,4 @@ private void selectAgain() { logger.warn("Failed to update SelectionKeys.", t); } } - - @Override - protected void validateScheduled(long amount, TimeUnit unit) { - long days = unit.toDays(amount); - if (days > MAX_SCHEDULED_DAYS) { - throw new IllegalArgumentException("days: " + days + " (expected: < " + MAX_SCHEDULED_DAYS + ')'); - } - } } diff --git a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java index 887dce421c99..124e0fe8a04a 100644 --- a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java +++ b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java @@ -73,27 +73,8 @@ public void run() { } } - @Test(timeout = 5000L) - public void testScheduleBigDelayOverMax() { - EventLoopGroup group = new NioEventLoopGroup(1); - final EventLoop el = group.next(); - try { - el.schedule(new Runnable() { - @Override - public void run() { - // NOOP - } - }, Integer.MAX_VALUE, TimeUnit.DAYS); - fail(); - } catch (IllegalArgumentException expected) { - // expected - } - - group.shutdownGracefully(); - } - @Test - public void testScheduleBigDelay() { + public void testScheduleBigDelayNotOverflow() { EventLoopGroup group = new NioEventLoopGroup(1); final EventLoop el = group.next(); @@ -102,7 +83,7 @@ public void testScheduleBigDelay() { public void run() { // NOOP } - }, NioEventLoop.MAX_SCHEDULED_DAYS, TimeUnit.DAYS); + }, Long.MAX_VALUE, TimeUnit.MILLISECONDS); assertFalse(future.awaitUninterruptibly(1000)); assertTrue(future.cancel(true)); From b53cf045a7718766da8edd9499c657e77b314bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=B6=E6=97=A0=E4=B8=A4=E4=B8=B6?= <442367943@qq.com> Date: Wed, 30 May 2018 21:33:39 +0800 Subject: [PATCH 034/417] SelectedSelectionKeySet should only be created if the set can be instrumented Motivation: If we can not replace the internal used Set of the Selector there is no need to create an SelectedSelectionKeySet instance. Modification: Only create SelectedSelectionKeySet if we will replace the internal set. Result: Less object creation in some cases and cleaner code. --- transport/src/main/java/io/netty/channel/nio/NioEventLoop.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index d71e3f955dd7..5e2217411204 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -179,8 +179,6 @@ private SelectorTuple openSelector() { return new SelectorTuple(unwrappedSelector); } - final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet(); - Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { @@ -206,6 +204,7 @@ public Object run() { } final Class selectorImplClass = (Class) maybeSelectorImplClass; + final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet(); Object maybeException = AccessController.doPrivileged(new PrivilegedAction() { @Override From 48911e0b633da526f37fb4e5bdcddf3f97c091b5 Mon Sep 17 00:00:00 2001 From: Nick Travers Date: Wed, 30 May 2018 10:52:40 -0700 Subject: [PATCH 035/417] Set (and override) websocket handshake headers after custom headers (#7975) Motivation: Currently, when passing custom headers to a WebSocketClientHandshaker, if values are added for headers that are reserved for use in the websocket handshake performed with the server, these custom values can be used by the server to compute the websocket handshake challenge. If the server computes the response to the challenge with the custom header values, rather than the values computed by the client handshaker, the handshake may fail. Modifications: Update the client handshaker implementations to add the custom header values first, and then set the reserved websocket header values. Result: Reserved websocket handshake headers, if present in the custom headers passed to the client handshaker, will not be propagated to the server. Instead the client handshaker will propagate the values it generates. Fixes #7973. --- .../WebSocketClientHandshaker00.java | 23 ++++----- .../WebSocketClientHandshaker07.java | 22 ++++----- .../WebSocketClientHandshaker08.java | 22 ++++----- .../WebSocketClientHandshaker13.java | 22 ++++----- .../WebSocketClientHandshaker00Test.java | 22 ++++++++- .../WebSocketClientHandshaker07Test.java | 22 ++++++++- .../WebSocketClientHandshaker08Test.java | 6 ++- .../WebSocketClientHandshaker13Test.java | 6 ++- .../WebSocketClientHandshakerTest.java | 47 +++++++++++++++++-- 9 files changed, 137 insertions(+), 55 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java index 05070f74b764..f02626319f32 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java @@ -131,22 +131,23 @@ protected FullHttpRequest newHandshakeRequest() { // Format request FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); HttpHeaders headers = request.headers(); - headers.add(HttpHeaderNames.UPGRADE, WEBSOCKET) - .add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .add(HttpHeaderNames.HOST, websocketHostValue(wsURL)) - .add(HttpHeaderNames.ORIGIN, websocketOriginValue(wsURL)) - .add(HttpHeaderNames.SEC_WEBSOCKET_KEY1, key1) - .add(HttpHeaderNames.SEC_WEBSOCKET_KEY2, key2); - - String expectedSubprotocol = expectedSubprotocol(); - if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { - headers.add(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); - } if (customHeaders != null) { headers.add(customHeaders); } + headers.set(HttpHeaderNames.UPGRADE, WEBSOCKET) + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) + .set(HttpHeaderNames.HOST, websocketHostValue(wsURL)) + .set(HttpHeaderNames.ORIGIN, websocketOriginValue(wsURL)) + .set(HttpHeaderNames.SEC_WEBSOCKET_KEY1, key1) + .set(HttpHeaderNames.SEC_WEBSOCKET_KEY2, key2); + + String expectedSubprotocol = expectedSubprotocol(); + if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { + headers.set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); + } + // Set Content-Length to workaround some known defect. // See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html headers.set(HttpHeaderNames.CONTENT_LENGTH, key3.length); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java index f85d086b2f49..4632a4aecb69 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java @@ -145,22 +145,22 @@ protected FullHttpRequest newHandshakeRequest() { FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); HttpHeaders headers = request.headers(); - headers.add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .add(HttpHeaderNames.SEC_WEBSOCKET_KEY, key) - .add(HttpHeaderNames.HOST, websocketHostValue(wsURL)) - .add(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, websocketOriginValue(wsURL)); + if (customHeaders != null) { + headers.add(customHeaders); + } + + headers.set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) + .set(HttpHeaderNames.SEC_WEBSOCKET_KEY, key) + .set(HttpHeaderNames.HOST, websocketHostValue(wsURL)) + .set(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, websocketOriginValue(wsURL)); String expectedSubprotocol = expectedSubprotocol(); if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { - headers.add(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); + headers.set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); } - headers.add(HttpHeaderNames.SEC_WEBSOCKET_VERSION, "7"); - - if (customHeaders != null) { - headers.add(customHeaders); - } + headers.set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, "7"); return request; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java index 5bfef6449c11..1a11aa6358e7 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java @@ -146,22 +146,22 @@ protected FullHttpRequest newHandshakeRequest() { FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); HttpHeaders headers = request.headers(); - headers.add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .add(HttpHeaderNames.SEC_WEBSOCKET_KEY, key) - .add(HttpHeaderNames.HOST, websocketHostValue(wsURL)) - .add(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, websocketOriginValue(wsURL)); + if (customHeaders != null) { + headers.add(customHeaders); + } + + headers.set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) + .set(HttpHeaderNames.SEC_WEBSOCKET_KEY, key) + .set(HttpHeaderNames.HOST, websocketHostValue(wsURL)) + .set(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, websocketOriginValue(wsURL)); String expectedSubprotocol = expectedSubprotocol(); if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { - headers.add(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); + headers.set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); } - headers.add(HttpHeaderNames.SEC_WEBSOCKET_VERSION, "8"); - - if (customHeaders != null) { - headers.add(customHeaders); - } + headers.set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, "8"); return request; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java index 9490e3bcef51..808f7fc49ad2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java @@ -146,22 +146,22 @@ protected FullHttpRequest newHandshakeRequest() { FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); HttpHeaders headers = request.headers(); - headers.add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .add(HttpHeaderNames.SEC_WEBSOCKET_KEY, key) - .add(HttpHeaderNames.HOST, websocketHostValue(wsURL)) - .add(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, websocketOriginValue(wsURL)); + if (customHeaders != null) { + headers.add(customHeaders); + } + + headers.set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) + .set(HttpHeaderNames.SEC_WEBSOCKET_KEY, key) + .set(HttpHeaderNames.HOST, websocketHostValue(wsURL)) + .set(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, websocketOriginValue(wsURL)); String expectedSubprotocol = expectedSubprotocol(); if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { - headers.add(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); + headers.set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); } - headers.add(HttpHeaderNames.SEC_WEBSOCKET_VERSION, "13"); - - if (customHeaders != null) { - headers.add(customHeaders); - } + headers.set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, "13"); return request; } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00Test.java index bda0734ad969..33c6ce6847e4 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00Test.java @@ -16,17 +16,35 @@ package io.netty.handler.codec.http.websocketx; import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaders; import java.net.URI; public class WebSocketClientHandshaker00Test extends WebSocketClientHandshakerTest { @Override - protected WebSocketClientHandshaker newHandshaker(URI uri) { - return new WebSocketClientHandshaker00(uri, WebSocketVersion.V00, null, null, 1024); + protected WebSocketClientHandshaker newHandshaker(URI uri, String subprotocol, HttpHeaders headers) { + return new WebSocketClientHandshaker00(uri, WebSocketVersion.V00, subprotocol, headers, 1024); } @Override protected CharSequence getOriginHeaderName() { return HttpHeaderNames.ORIGIN; } + + @Override + protected CharSequence getProtocolHeaderName() { + return HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL; + } + + @Override + protected CharSequence[] getHandshakeHeaderNames() { + return new CharSequence[] { + HttpHeaderNames.CONNECTION, + HttpHeaderNames.UPGRADE, + HttpHeaderNames.HOST, + HttpHeaderNames.ORIGIN, + HttpHeaderNames.SEC_WEBSOCKET_KEY1, + HttpHeaderNames.SEC_WEBSOCKET_KEY2, + }; + } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07Test.java index bce6c73a78ff..9ff3e8485b90 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07Test.java @@ -16,17 +16,35 @@ package io.netty.handler.codec.http.websocketx; import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaders; import java.net.URI; public class WebSocketClientHandshaker07Test extends WebSocketClientHandshakerTest { @Override - protected WebSocketClientHandshaker newHandshaker(URI uri) { - return new WebSocketClientHandshaker07(uri, WebSocketVersion.V07, null, false, null, 1024); + protected WebSocketClientHandshaker newHandshaker(URI uri, String subprotocol, HttpHeaders headers) { + return new WebSocketClientHandshaker07(uri, WebSocketVersion.V07, subprotocol, false, headers, 1024); } @Override protected CharSequence getOriginHeaderName() { return HttpHeaderNames.SEC_WEBSOCKET_ORIGIN; } + + @Override + protected CharSequence getProtocolHeaderName() { + return HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL; + } + + @Override + protected CharSequence[] getHandshakeHeaderNames() { + return new CharSequence[] { + HttpHeaderNames.UPGRADE, + HttpHeaderNames.CONNECTION, + HttpHeaderNames.SEC_WEBSOCKET_KEY, + HttpHeaderNames.HOST, + HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, + HttpHeaderNames.SEC_WEBSOCKET_VERSION, + }; + } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08Test.java index 0af3030f3319..1efb6821b9b6 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08Test.java @@ -15,11 +15,13 @@ */ package io.netty.handler.codec.http.websocketx; +import io.netty.handler.codec.http.HttpHeaders; + import java.net.URI; public class WebSocketClientHandshaker08Test extends WebSocketClientHandshaker07Test { @Override - protected WebSocketClientHandshaker newHandshaker(URI uri) { - return new WebSocketClientHandshaker08(uri, WebSocketVersion.V08, null, false, null, 1024); + protected WebSocketClientHandshaker newHandshaker(URI uri, String subprotocol, HttpHeaders headers) { + return new WebSocketClientHandshaker08(uri, WebSocketVersion.V08, subprotocol, false, headers, 1024); } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13Test.java index ad89fde6bc12..1727178831df 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13Test.java @@ -15,11 +15,13 @@ */ package io.netty.handler.codec.http.websocketx; +import io.netty.handler.codec.http.HttpHeaders; + import java.net.URI; public class WebSocketClientHandshaker13Test extends WebSocketClientHandshaker07Test { @Override - protected WebSocketClientHandshaker newHandshaker(URI uri) { - return new WebSocketClientHandshaker13(uri, WebSocketVersion.V13, null, false, null, 1024); + protected WebSocketClientHandshaker newHandshaker(URI uri, String subprotocol, HttpHeaders headers) { + return new WebSocketClientHandshaker13(uri, WebSocketVersion.V13, subprotocol, false, headers, 1024); } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerTest.java index eeb0d69d1dc2..de1594b85823 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerTest.java @@ -21,11 +21,13 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.EmptyHttpHeaders; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequestEncoder; import io.netty.handler.codec.http.HttpResponseDecoder; @@ -35,14 +37,21 @@ import java.net.URI; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public abstract class WebSocketClientHandshakerTest { - protected abstract WebSocketClientHandshaker newHandshaker(URI uri); + protected abstract WebSocketClientHandshaker newHandshaker(URI uri, String subprotocol, HttpHeaders headers); + + protected WebSocketClientHandshaker newHandshaker(URI uri) { + return newHandshaker(uri, null, null); + } protected abstract CharSequence getOriginHeaderName(); + protected abstract CharSequence getProtocolHeaderName(); + + protected abstract CharSequence[] getHandshakeHeaderNames(); + @Test public void hostHeaderWs() { for (String scheme : new String[]{"ws://", "http://"}) { @@ -292,4 +301,36 @@ protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) thr frame.release(); } } + + @Test + public void testDuplicateWebsocketHandshakeHeaders() { + URI uri = URI.create("ws://localhost:9999/foo"); + + HttpHeaders inputHeaders = new DefaultHttpHeaders(); + String bogusSubProtocol = "bogusSubProtocol"; + String bogusHeaderValue = "bogusHeaderValue"; + + // add values for the headers that are reserved for use in the websockets handshake + for (CharSequence header : getHandshakeHeaderNames()) { + inputHeaders.add(header, bogusHeaderValue); + } + inputHeaders.add(getProtocolHeaderName(), bogusSubProtocol); + + String realSubProtocol = "realSubProtocol"; + WebSocketClientHandshaker handshaker = newHandshaker(uri, realSubProtocol, inputHeaders); + FullHttpRequest request = handshaker.newHandshakeRequest(); + HttpHeaders outputHeaders = request.headers(); + + // the header values passed in originally have been replaced with values generated by the Handshaker + for (CharSequence header : getHandshakeHeaderNames()) { + assertEquals(1, outputHeaders.getAll(header).size()); + assertNotEquals(bogusHeaderValue, outputHeaders.get(header)); + } + + // the subprotocol header value is that of the subprotocol string passed into the Handshaker + assertEquals(1, outputHeaders.getAll(getProtocolHeaderName()).size()); + assertEquals(realSubProtocol, outputHeaders.get(getProtocolHeaderName())); + + request.release(); + } } From a4393831f0c95e4b89812238b9ac26ac4322e451 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 30 May 2018 22:07:42 +0200 Subject: [PATCH 036/417] Fix race in SslHandlerTest that could lead to NPE. (#7989) Motivation: SslHandlerTest tried to get access to the SslHandler in the pipeline via pipeline.get(...) which may return null if the channel was already closed and so the pipeline was teared down. This showed up in a test run as: ``` ------------------------------------------------------------------------------- Test set: io.netty.handler.ssl.SslHandlerTest ------------------------------------------------------------------------------- Tests run: 17, Failures: 0, Errors: 1, Skipped: 1, Time elapsed: 0.802 sec <<< FAILURE! - in io.netty.handler.ssl.SslHandlerTest testCloseOnHandshakeFailure(io.netty.handler.ssl.SslHandlerTest) Time elapsed: 0.188 sec <<< ERROR! java.lang.NullPointerException at io.netty.handler.ssl.SslHandlerTest.testCloseOnHandshakeFailure(SslHandlerTest.java:640) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:564) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:298) at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:292) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.lang.Thread.run(Thread.java:844) ``` Modifications: Use an AtomicReference to propagate the SslHandler instance to the outer scope. Result: No more NPE. --- .../java/io/netty/handler/ssl/SslHandlerTest.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java index 02cc3ee5b5b0..e982b6a63c3e 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java @@ -64,6 +64,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; @@ -626,17 +627,24 @@ protected void initChannel(Channel ch) { }); sc = sb.bind(address).syncUninterruptibly().channel(); + final AtomicReference sslHandlerRef = new AtomicReference(); Bootstrap b = new Bootstrap() .group(group) .channel(LocalChannel.class) .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) { - ch.pipeline().addLast(sslClientCtx.newHandler(ch.alloc())); + SslHandler handler = sslClientCtx.newHandler(ch.alloc()); + + // We propagate the SslHandler via an AtomicReference to the outer-scope as using + // pipeline.get(...) may return null if the pipeline was teared down by the time we call it. + // This will happen if the channel was closed in the meantime. + sslHandlerRef.set(handler); + ch.pipeline().addLast(handler); } }); cc = b.connect(sc.localAddress()).syncUninterruptibly().channel(); - SslHandler handler = cc.pipeline().get(SslHandler.class); + SslHandler handler = sslHandlerRef.get(); handler.handshakeFuture().awaitUninterruptibly(); assertFalse(handler.handshakeFuture().isSuccess()); From 1611acf4cee4481b89a2cf024ccf821de2dbf13c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 1 Jun 2018 21:15:08 +0200 Subject: [PATCH 037/417] Fix CharSequenceValueConverter.convertToByte implementation for AsciiString (#7994) Motivation: The implementation of CharSequenceValueConverter.convertToByte did not correctly handle AsciiString if the length != 1. Modifications: - Only use fast-path for AsciiString with length of 1. - Add unit tests. Result: Fixes https://github.com/netty/netty/issues/7990 --- .../handler/codec/CharSequenceValueConverter.java | 2 +- .../handler/codec/CharSequenceValueConverterTest.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/codec/src/main/java/io/netty/handler/codec/CharSequenceValueConverter.java b/codec/src/main/java/io/netty/handler/codec/CharSequenceValueConverter.java index b6510d1ff7e0..1157af0623e5 100644 --- a/codec/src/main/java/io/netty/handler/codec/CharSequenceValueConverter.java +++ b/codec/src/main/java/io/netty/handler/codec/CharSequenceValueConverter.java @@ -77,7 +77,7 @@ public CharSequence convertByte(byte value) { @Override public byte convertToByte(CharSequence value) { - if (value instanceof AsciiString) { + if (value instanceof AsciiString && value.length() == 1) { return ((AsciiString) value).byteAt(0); } return Byte.parseByte(value.toString()); diff --git a/codec/src/test/java/io/netty/handler/codec/CharSequenceValueConverterTest.java b/codec/src/test/java/io/netty/handler/codec/CharSequenceValueConverterTest.java index 5543e2f90955..2347f0d0bf9b 100644 --- a/codec/src/test/java/io/netty/handler/codec/CharSequenceValueConverterTest.java +++ b/codec/src/test/java/io/netty/handler/codec/CharSequenceValueConverterTest.java @@ -14,6 +14,7 @@ */ package io.netty.handler.codec; +import io.netty.util.AsciiString; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -30,6 +31,16 @@ public void testBoolean() { assertFalse(converter.convertToBoolean(converter.convertBoolean(false))); } + @Test + public void testByteFromAsciiString() { + assertEquals(127, converter.convertToByte(AsciiString.of("127"))); + } + + @Test(expected = NumberFormatException.class) + public void testByteFromEmptyAsciiString() { + converter.convertToByte(AsciiString.EMPTY_STRING); + } + @Test public void testByte() { assertEquals(Byte.MAX_VALUE, converter.convertToByte(converter.convertByte(Byte.MAX_VALUE))); From 6208c9b0d66b4e28f371f60342bdeadbcb3b745a Mon Sep 17 00:00:00 2001 From: Roger Date: Mon, 4 Jun 2018 04:14:58 -0400 Subject: [PATCH 038/417] Mention the HttpObjectEncoder's state in the message of the IllegalStateException (#7996) Motivation The HttpObjectEncoder raises an IllegalStateException due to an illegal state but doesn't mention what the state was. It could be useful for debugging purposes to figure out what happened. Modifications Mention the HttpObjectEncoder's state in the message of the IllegalStateException. Result An exception with more information what caused it. --- .../java/io/netty/handler/codec/http/HttpObjectEncoder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java index fe03378bba6e..5841dc1e1528 100755 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java @@ -83,7 +83,8 @@ protected void encode(ChannelHandlerContext ctx, Object msg, List out) t ByteBuf buf = null; if (msg instanceof HttpMessage) { if (state != ST_INIT) { - throw new IllegalStateException("unexpected message type: " + StringUtil.simpleClassName(msg)); + throw new IllegalStateException("unexpected message type: " + StringUtil.simpleClassName(msg) + + ", state: " + state); } @SuppressWarnings({ "unchecked", "CastConflictsWithInstanceof" }) From 00786337029d48d2e5815ff5076284a46373062b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 4 Jun 2018 18:09:42 +0200 Subject: [PATCH 039/417] Use java 11+ea16 (#7999) Motivation: Java 11+ea16 was released. Modifications: Update to latest version. Result: Testing with latest java 11 release. --- docker/docker-compose.centos-6.111.yaml | 2 +- docker/docker-compose.centos-7.111.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml index c5a8103a1e75..b4d2dcba9769 100644 --- a/docker/docker-compose.centos-6.111.yaml +++ b/docker/docker-compose.centos-6.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "1.11.0-15" + java_version : "1.11.0-16" test: image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-7.111.yaml b/docker/docker-compose.centos-7.111.yaml index ee069d99bf5f..6205e3988614 100644 --- a/docker/docker-compose.centos-7.111.yaml +++ b/docker/docker-compose.centos-7.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "1.11.0-15" + java_version : "1.11.0-16" test: image: netty:centos-7-1.11 From b192bf12ad2af92bba0f32c9d3127b1192e54670 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 4 Jun 2018 20:40:08 +0200 Subject: [PATCH 040/417] Update conscrypt to 1.1.3 which fixes some NPEs during tests when using conscrypt. (#8001) Motivation: When using conscrypt some NPEs were logged, these were fixed in the latest release. Modifications: Update to conscrypt 1.1.3. Result: Fixes https://github.com/netty/netty/issues/7988. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4cb89d220c50..71e3e4b2da75 100644 --- a/pom.xml +++ b/pom.xml @@ -225,7 +225,7 @@ ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber - 1.1.2 + 1.1.3 ${os.detected.name}-${os.detected.arch} ${project.basedir}/../common/src/test/resources/logback-test.xml From abe77511b99b93a7ec98ddea78243ff6e6235107 Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Thu, 7 Jun 2018 16:53:21 -0600 Subject: [PATCH 041/417] Remove dead code in Http2CodecUtil (#8009) Motivation: The `ByteBuffer emptyPingBuf()` method of Http2CodecUtils is has been dead code since DefaultHttp2PingFrame switched from using a ByteBuf to represent the 8 octets to a long. Modifications: Remove the method and the unused static ByteBuf. Result: Less dead code. Fixes #8002 --- .../io/netty/handler/codec/http2/Http2CodecUtil.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2CodecUtil.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2CodecUtil.java index 317ee48063d8..7ebc8fd48888 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2CodecUtil.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2CodecUtil.java @@ -67,9 +67,6 @@ public final class Http2CodecUtil { private static final ByteBuf CONNECTION_PREFACE = unreleasableBuffer(directBuffer(24).writeBytes("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(UTF_8))) .asReadOnly(); - private static final ByteBuf EMPTY_PING = - unreleasableBuffer(directBuffer(PING_FRAME_PAYLOAD_LENGTH).writeZero(PING_FRAME_PAYLOAD_LENGTH)) - .asReadOnly(); private static final int MAX_PADDING_LENGTH_LENGTH = 1; public static final int DATA_FRAME_HEADER_LENGTH = FRAME_HEADER_LENGTH + MAX_PADDING_LENGTH_LENGTH; @@ -169,14 +166,6 @@ public static ByteBuf connectionPrefaceBuf() { return CONNECTION_PREFACE.retainedDuplicate(); } - /** - * Returns a buffer filled with all zeros that is the appropriate length for a PING frame. - */ - public static ByteBuf emptyPingBuf() { - // Return a duplicate so that modifications to the reader index will not affect the original buffer. - return EMPTY_PING.retainedDuplicate(); - } - /** * Iteratively looks through the causality chain for the given exception and returns the first * {@link Http2Exception} or {@code null} if none. From 400ca8733427732e426d6fa0eb88fcfe7e06b06d Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Thu, 7 Jun 2018 17:01:41 -0600 Subject: [PATCH 042/417] Provide an API for controlling and h2c upgrade response stream in Http2MultiplexCodec (#7968) Motivation: Http2MultiplexCodec doesn't currently have an API for using the response of a h2c upgrade request. Modifications: Add a new API to the Http2MultiplexCodecBuilder which allows for setting an upgrade handler and wire it into the Http2MultiplexCodec implementation. Result: When using the Http2MultiplexCodec with h2c upgrades the upgrade handler will get added to the Http2StreamChannel which represents the half-closed (local) response of stream 1. It is then up to the user to manage the transition from the IO channel pipeline configuration necessary for making the h2c upgrade request to a form where it can read the response from the new stream channel. Fixes #7947. --- .../handler/codec/http2/Http2FrameCodec.java | 2 +- .../codec/http2/Http2MultiplexCodec.java | 39 ++++++++- .../http2/Http2MultiplexCodecBuilder.java | 11 ++- .../http2/Http2ClientUpgradeCodecTest.java | 5 +- .../Http2MultiplexCodecClientUpgradeTest.java | 81 +++++++++++++++++++ .../codec/http2/Http2MultiplexCodecTest.java | 2 +- 6 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecClientUpgradeTest.java diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java index da2b27a1d7bd..47f2533f3f81 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java @@ -143,7 +143,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler { private static final InternalLogger LOG = InternalLoggerFactory.getInstance(Http2FrameCodec.class); - private final PropertyKey streamKey; + protected final PropertyKey streamKey; private final PropertyKey upgradeKey; private final Integer initialFlowControlWindowSize; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java index 515b21d495de..5f17c7503f9f 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java @@ -49,7 +49,11 @@ import java.util.ArrayDeque; import java.util.Queue; +import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_STREAM_ID; import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid; +import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; +import static io.netty.handler.codec.http2.Http2Exception.connectionError; + import static java.lang.Math.min; /** @@ -153,6 +157,7 @@ public int guess() { } private final ChannelHandler inboundStreamHandler; + private final ChannelHandler upgradeStreamHandler; private int initialOutboundStreamWindow = Http2CodecUtil.DEFAULT_WINDOW_SIZE; private boolean parentReadInProgress; @@ -168,9 +173,25 @@ public int guess() { Http2MultiplexCodec(Http2ConnectionEncoder encoder, Http2ConnectionDecoder decoder, Http2Settings initialSettings, - ChannelHandler inboundStreamHandler) { + ChannelHandler inboundStreamHandler, + ChannelHandler upgradeStreamHandler) { super(encoder, decoder, initialSettings); this.inboundStreamHandler = inboundStreamHandler; + this.upgradeStreamHandler = upgradeStreamHandler; + } + + @Override + public void onHttpClientUpgrade() throws Http2Exception { + // We must have an upgrade handler or else we can't handle the stream + if (upgradeStreamHandler == null) { + throw connectionError(INTERNAL_ERROR, "Client is misconfigured for upgrade requests"); + } + // Creates the Http2Stream in the Connection. + super.onHttpClientUpgrade(); + // Now make a new FrameStream, set it's underlying Http2Stream, and initialize it. + Http2MultiplexCodecStream codecStream = newStream(); + codecStream.setStreamAndProperty(streamKey, connection().stream(HTTP_UPGRADE_STREAM_ID)); + onHttp2UpgradeStreamInitialized(ctx, codecStream); } private static void registerDone(ChannelFuture future) { @@ -236,6 +257,22 @@ final void onHttp2Frame(ChannelHandlerContext ctx, Http2Frame frame) { } } + private void onHttp2UpgradeStreamInitialized(ChannelHandlerContext ctx, Http2MultiplexCodecStream stream) { + assert stream.state() == Http2Stream.State.HALF_CLOSED_LOCAL; + DefaultHttp2StreamChannel ch = new DefaultHttp2StreamChannel(stream, true); + ch.outboundClosed = true; + + // Add our upgrade handler to the channel and then register the channel. + // The register call fires the channelActive, etc. + ch.pipeline().addLast(upgradeStreamHandler); + ChannelFuture future = ctx.channel().eventLoop().register(ch); + if (future.isDone()) { + registerDone(future); + } else { + future.addListener(CHILD_CHANNEL_REGISTRATION_LISTENER); + } + } + @Override final void onHttp2StreamStateChanged(ChannelHandlerContext ctx, Http2FrameStream stream) { Http2MultiplexCodecStream s = (Http2MultiplexCodecStream) stream; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java index 8e0929094a6b..94f0d4972038 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java @@ -29,6 +29,7 @@ public class Http2MultiplexCodecBuilder extends AbstractHttp2ConnectionHandlerBuilder { final ChannelHandler childHandler; + private ChannelHandler upgradeStreamHandler; Http2MultiplexCodecBuilder(boolean server, ChannelHandler childHandler) { server(server); @@ -83,6 +84,14 @@ public Http2MultiplexCodecBuilder gracefulShutdownTimeoutMillis(long gracefulShu return super.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis); } + public Http2MultiplexCodecBuilder withUpgradeStreamHandler(ChannelHandler upgradeStreamHandler) { + if (this.isServer()) { + throw new IllegalArgumentException("Server codecs don't use an extra handler for the upgrade stream"); + } + this.upgradeStreamHandler = upgradeStreamHandler; + return this; + } + @Override public boolean isServer() { return super.isServer(); @@ -157,6 +166,6 @@ public Http2MultiplexCodec build() { @Override protected Http2MultiplexCodec build( Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, Http2Settings initialSettings) { - return new Http2MultiplexCodec(encoder, decoder, initialSettings, childHandler); + return new Http2MultiplexCodec(encoder, decoder, initialSettings, childHandler, upgradeStreamHandler); } } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodecTest.java index cb774a94ca94..fcfdb4b75e54 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodecTest.java @@ -14,9 +14,11 @@ */ package io.netty.handler.codec.http2; +import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; @@ -43,7 +45,8 @@ public void testUpgradeToHttp2FrameCodec() throws Exception { @Test public void testUpgradeToHttp2MultiplexCodec() throws Exception { - testUpgrade(Http2MultiplexCodecBuilder.forClient(new HttpInboundHandler()).build()); + testUpgrade(Http2MultiplexCodecBuilder.forClient(new HttpInboundHandler()) + .withUpgradeStreamHandler(new ChannelInboundHandlerAdapter()).build()); } private static void testUpgrade(Http2ConnectionHandler handler) throws Exception { diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecClientUpgradeTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecClientUpgradeTest.java new file mode 100644 index 000000000000..26b63ed7f9c3 --- /dev/null +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecClientUpgradeTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package io.netty.handler.codec.http2; + +import org.junit.Test; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.embedded.EmbeddedChannel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class Http2MultiplexCodecClientUpgradeTest { + + @ChannelHandler.Sharable + private final class NoopHandler extends ChannelInboundHandlerAdapter { + @Override + public void channelActive(ChannelHandlerContext ctx) { + ctx.channel().close(); + } + } + + private final class UpgradeHandler extends ChannelInboundHandlerAdapter { + Http2Stream.State stateOnActive; + int streamId; + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + Http2StreamChannel ch = (Http2StreamChannel) ctx.channel(); + stateOnActive = ch.stream().state(); + streamId = ch.stream().id(); + super.channelActive(ctx); + } + } + + private Http2MultiplexCodec newCodec(ChannelHandler upgradeHandler) { + Http2MultiplexCodecBuilder builder = Http2MultiplexCodecBuilder.forClient(new NoopHandler()); + builder.withUpgradeStreamHandler(upgradeHandler); + return builder.build(); + } + + @Test + public void upgradeHandlerGetsActivated() throws Exception { + UpgradeHandler upgradeHandler = new UpgradeHandler(); + Http2MultiplexCodec codec = newCodec(upgradeHandler); + EmbeddedChannel ch = new EmbeddedChannel(codec); + + codec.onHttpClientUpgrade(); + + assertFalse(upgradeHandler.stateOnActive.localSideOpen()); + assertTrue(upgradeHandler.stateOnActive.remoteSideOpen()); + assertEquals(1, upgradeHandler.streamId); + assertTrue(ch.finishAndReleaseAll()); + } + + @Test(expected = Http2Exception.class) + public void clientUpgradeWithoutUpgradeHandlerThrowsHttp2Exception() throws Http2Exception { + Http2MultiplexCodec codec = Http2MultiplexCodecBuilder.forClient(new NoopHandler()).build(); + EmbeddedChannel ch = new EmbeddedChannel(codec); + try { + codec.onHttpClientUpgrade(); + } finally { + assertTrue(ch.finishAndReleaseAll()); + } + } +} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java index f07b218e0950..9e455909c95d 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java @@ -670,7 +670,7 @@ public TestableHttp2MultiplexCodec(Http2ConnectionEncoder encoder, Http2ConnectionDecoder decoder, Http2Settings initialSettings, ChannelHandler inboundStreamHandler) { - super(encoder, decoder, initialSettings, inboundStreamHandler); + super(encoder, decoder, initialSettings, inboundStreamHandler, null); } void onHttp2Frame(Http2Frame frame) { From 35215309b97804334610a56e5f5a7660db9c9017 Mon Sep 17 00:00:00 2001 From: Tim Brooks Date: Wed, 13 Jun 2018 12:43:31 -0600 Subject: [PATCH 043/417] Make UnpooledHeapByteBuf array methods protected (#8015) Motivation: Currently there is not a clear way to provide a byte array to a netty ByteBuf and be informed when it is released. This is a would be a valuable addition for projects that integrate with netty but also pool their own byte arrays. Modification: Modified the UnpooledHeapByteBuf class so that the freeArray method is protected visibility instead of default. This will allow a user to subclass the UnpooledHeapByteBuf, provide a byte array, and override freeArray to return the byte array to a pool when it is called. Additionally this makes this implementation equivalent to UnpooledDirectByteBuf (freeDirect is protected). Additionally allocateArray is also made protect to provide another override option for subclasses. Result: Users can override UnpooledHeapByteBuf#freeArray and UnpooledHeapByteBuf#allocateArray. --- .../java/io/netty/buffer/UnpooledByteBufAllocator.java | 8 ++++---- .../main/java/io/netty/buffer/UnpooledHeapByteBuf.java | 4 ++-- .../java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java index 4edf0dcd3d93..6fe188ed8bdb 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java @@ -140,14 +140,14 @@ private static final class InstrumentedUnpooledUnsafeHeapByteBuf extends Unpoole } @Override - byte[] allocateArray(int initialCapacity) { + protected byte[] allocateArray(int initialCapacity) { byte[] bytes = super.allocateArray(initialCapacity); ((UnpooledByteBufAllocator) alloc()).incrementHeap(bytes.length); return bytes; } @Override - void freeArray(byte[] array) { + protected void freeArray(byte[] array) { int length = array.length; super.freeArray(array); ((UnpooledByteBufAllocator) alloc()).decrementHeap(length); @@ -160,14 +160,14 @@ private static final class InstrumentedUnpooledHeapByteBuf extends UnpooledHeapB } @Override - byte[] allocateArray(int initialCapacity) { + protected byte[] allocateArray(int initialCapacity) { byte[] bytes = super.allocateArray(initialCapacity); ((UnpooledByteBufAllocator) alloc()).incrementHeap(bytes.length); return bytes; } @Override - void freeArray(byte[] array) { + protected void freeArray(byte[] array) { int length = array.length; super.freeArray(array); ((UnpooledByteBufAllocator) alloc()).decrementHeap(length); diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java index 26b1c5122b5d..0133845f822c 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java @@ -84,11 +84,11 @@ protected UnpooledHeapByteBuf(ByteBufAllocator alloc, byte[] initialArray, int m setIndex(0, initialArray.length); } - byte[] allocateArray(int initialCapacity) { + protected byte[] allocateArray(int initialCapacity) { return new byte[initialCapacity]; } - void freeArray(byte[] array) { + protected void freeArray(byte[] array) { // NOOP } diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java index 51786f1567c4..0fbe856466f1 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java @@ -30,7 +30,7 @@ class UnpooledUnsafeHeapByteBuf extends UnpooledHeapByteBuf { } @Override - byte[] allocateArray(int initialCapacity) { + protected byte[] allocateArray(int initialCapacity) { return PlatformDependent.allocateUninitializedArray(initialCapacity); } From 3e3e5155b9bb4375fc4c95ac53c5c2ed8e8c5446 Mon Sep 17 00:00:00 2001 From: Roger Date: Thu, 14 Jun 2018 02:21:53 -0400 Subject: [PATCH 044/417] Check if Log level is enabled before creating log statements (#8022) Motivation There is a cost to concatenating strings and calling methods that will be wasted if the Logger's level is not enabled. Modifications Check if Log level is enabled before producing log statement. These are just a few cases found by RegEx'ing in the code. Result Tiny bit more efficient code. --- .../main/java/io/netty/util/HashedWheelTimer.java | 10 ++++++---- .../io/netty/util/concurrent/DefaultPromise.java | 8 ++++++-- .../concurrent/SingleThreadEventExecutor.java | 15 +++++++++------ .../ssl/util/InsecureTrustManagerFactory.java | 8 ++++++-- .../handler/ssl/util/SelfSignedCertificate.java | 12 +++++++++--- .../netty/handler/stream/ChunkedWriteHandler.java | 4 +++- .../traffic/AbstractTrafficShapingHandler.java | 12 ++++++++---- .../java/io/netty/resolver/HostsFileParser.java | 4 +++- .../io/netty/channel/sctp/oio/OioSctpChannel.java | 4 +++- .../java/io/netty/channel/ChannelInitializer.java | 4 +++- .../java/io/netty/channel/nio/NioEventLoop.java | 4 +++- 11 files changed, 59 insertions(+), 26 deletions(-) diff --git a/common/src/main/java/io/netty/util/HashedWheelTimer.java b/common/src/main/java/io/netty/util/HashedWheelTimer.java index b62ace4695ed..e0afbe67cec6 100644 --- a/common/src/main/java/io/netty/util/HashedWheelTimer.java +++ b/common/src/main/java/io/netty/util/HashedWheelTimer.java @@ -437,10 +437,12 @@ public long pendingTimeouts() { } private static void reportTooManyInstances() { - String resourceType = simpleClassName(HashedWheelTimer.class); - logger.error("You are creating too many " + resourceType + " instances. " + - resourceType + " is a shared resource that must be reused across the JVM," + - "so that only a few instances are created."); + if (logger.isErrorEnabled()) { + String resourceType = simpleClassName(HashedWheelTimer.class); + logger.error("You are creating too many " + resourceType + " instances. " + + resourceType + " is a shared resource that must be reused across the JVM," + + "so that only a few instances are created."); + } } private final class Worker implements Runnable { diff --git a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java b/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java index 2f4dc7999330..ca67ccfbaaae 100644 --- a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java +++ b/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java @@ -510,7 +510,9 @@ private static void notifyListener0(Future future, GenericFutureListener l) { try { l.operationComplete(future); } catch (Throwable t) { - logger.warn("An exception was thrown by " + l.getClass().getName() + ".operationComplete()", t); + if (logger.isWarnEnabled()) { + logger.warn("An exception was thrown by " + l.getClass().getName() + ".operationComplete()", t); + } } } @@ -740,7 +742,9 @@ private static void notifyProgressiveListener0( try { l.operationProgressed(future, progress, total); } catch (Throwable t) { - logger.warn("An exception was thrown by " + l.getClass().getName() + ".operationProgressed()", t); + if (logger.isWarnEnabled()) { + logger.warn("An exception was thrown by " + l.getClass().getName() + ".operationProgressed()", t); + } } } diff --git a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java index 09197e6fdb0d..bb51d8f39629 100644 --- a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java +++ b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java @@ -896,9 +896,11 @@ public void run() { // Check if confirmShutdown() was called at the end of the loop. if (success && gracefulShutdownStartTime == 0) { - logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " + - SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called " + - "before run() implementation terminates."); + if (logger.isErrorEnabled()) { + logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " + + SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must " + + "be called before run() implementation terminates."); + } } try { @@ -915,9 +917,10 @@ public void run() { STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED); threadLock.release(); if (!taskQueue.isEmpty()) { - logger.warn( - "An event executor terminated with " + - "non-empty task queue (" + taskQueue.size() + ')'); + if (logger.isWarnEnabled()) { + logger.warn("An event executor terminated with " + + "non-empty task queue (" + taskQueue.size() + ')'); + } } terminationFuture.setSuccess(null); diff --git a/handler/src/main/java/io/netty/handler/ssl/util/InsecureTrustManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/util/InsecureTrustManagerFactory.java index d78b4686772a..23ce8f6af8b4 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/InsecureTrustManagerFactory.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/InsecureTrustManagerFactory.java @@ -44,12 +44,16 @@ public final class InsecureTrustManagerFactory extends SimpleTrustManagerFactory private static final TrustManager tm = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String s) { - logger.debug("Accepting a client certificate: " + chain[0].getSubjectDN()); + if (logger.isDebugEnabled()) { + logger.debug("Accepting a client certificate: " + chain[0].getSubjectDN()); + } } @Override public void checkServerTrusted(X509Certificate[] chain, String s) { - logger.debug("Accepting a server certificate: " + chain[0].getSubjectDN()); + if (logger.isDebugEnabled()) { + logger.debug("Accepting a server certificate: " + chain[0].getSubjectDN()); + } } @Override diff --git a/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java b/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java index 112e1a88a094..9f010ce8ec72 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java @@ -175,7 +175,9 @@ public SelfSignedCertificate(String fqdn, SecureRandom random, int bits, Date no try { certificateInput.close(); } catch (IOException e) { - logger.warn("Failed to close a file: " + certificate, e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a file: " + certificate, e); + } } } } @@ -288,7 +290,9 @@ static String[] newSelfSignedCertificate( private static void safeDelete(File certFile) { if (!certFile.delete()) { - logger.warn("Failed to delete a file: " + certFile); + if (logger.isWarnEnabled()) { + logger.warn("Failed to delete a file: " + certFile); + } } } @@ -296,7 +300,9 @@ private static void safeClose(File keyFile, OutputStream keyOut) { try { keyOut.close(); } catch (IOException e) { - logger.warn("Failed to close a file: " + keyFile, e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a file: " + keyFile, e); + } } } } diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java index 6f753c5c5100..b4805d98ef0c 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java @@ -178,7 +178,9 @@ private void discard(Throwable cause) { closeInput(in); } catch (Exception e) { currentWrite.fail(e); - logger.warn(ChunkedInput.class.getSimpleName() + ".isEndOfInput() failed", e); + if (logger.isWarnEnabled()) { + logger.warn(ChunkedInput.class.getSimpleName() + ".isEndOfInput() failed", e); + } closeInput(in); } } else { diff --git a/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java b/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java index caeb767fcf19..09e0f38798e6 100644 --- a/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java +++ b/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java @@ -441,11 +441,15 @@ public void run() { // Anything else allows the handler to reset the AutoRead if (logger.isDebugEnabled()) { if (config.isAutoRead() && !isHandlerActive(ctx)) { - logger.debug("Unsuspend: " + config.isAutoRead() + ':' + - isHandlerActive(ctx)); + if (logger.isDebugEnabled()) { + logger.debug("Unsuspend: " + config.isAutoRead() + ':' + + isHandlerActive(ctx)); + } } else { - logger.debug("Normal unsuspend: " + config.isAutoRead() + ':' - + isHandlerActive(ctx)); + if (logger.isDebugEnabled()) { + logger.debug("Normal unsuspend: " + config.isAutoRead() + ':' + + isHandlerActive(ctx)); + } } } channel.attr(READ_SUSPENDED).set(false); diff --git a/resolver/src/main/java/io/netty/resolver/HostsFileParser.java b/resolver/src/main/java/io/netty/resolver/HostsFileParser.java index 8ee1d195689f..1997fe9e1652 100644 --- a/resolver/src/main/java/io/netty/resolver/HostsFileParser.java +++ b/resolver/src/main/java/io/netty/resolver/HostsFileParser.java @@ -75,7 +75,9 @@ public static HostsFileEntries parseSilently() { try { return parse(hostsFile); } catch (IOException e) { - logger.warn("Failed to load and parse hosts file at " + hostsFile.getPath(), e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to load and parse hosts file at " + hostsFile.getPath(), e); + } return HostsFileEntries.EMPTY; } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java index f7c446d42c8e..658e0978c6e2 100755 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java @@ -402,7 +402,9 @@ private static void closeSelector(String selectorName, Selector selector) { try { selector.close(); } catch (IOException e) { - logger.warn("Failed to close a " + selectorName + " selector.", e); + if (logger.isWarnEnabled()) { + logger.warn("Failed to close a " + selectorName + " selector.", e); + } } } diff --git a/transport/src/main/java/io/netty/channel/ChannelInitializer.java b/transport/src/main/java/io/netty/channel/ChannelInitializer.java index 1d8578c3dd2b..9ea1b1822190 100644 --- a/transport/src/main/java/io/netty/channel/ChannelInitializer.java +++ b/transport/src/main/java/io/netty/channel/ChannelInitializer.java @@ -88,7 +88,9 @@ public final void channelRegistered(ChannelHandlerContext ctx) throws Exception */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), cause); + if (logger.isWarnEnabled()) { + logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), cause); + } ctx.close(); } diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index 5e2217411204..2c197775a921 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -394,7 +394,9 @@ private void rebuildSelector0() { } } - logger.info("Migrated " + nChannels + " channel(s) to the new Selector."); + if (logger.isInfoEnabled()) { + logger.info("Migrated " + nChannels + " channel(s) to the new Selector."); + } } @Override From c7c8e6a3ec4294f34c3788e9a8b0ce809b75ca61 Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Fri, 15 Jun 2018 00:03:37 -0600 Subject: [PATCH 045/417] Defer channelInactive and channelUnregistered events in Http2MultiplexCodec (#8021) Motivation: There is an inconsistency between the order of events in the StreamChannel implementation in Http2MultiplexCodec and other Channel implementations that extend AbstractChannel where channelInactive and channelUnregistered events are not performed 'later'. This can cause an unexected order of events for ChannelHandler implementations that call Channel.close() in response to some event. Modification: The Http2MultiplexCodec.DefaultHttp2StreamChannel.Http2ChannelUnsafe was modified to bounce the deregistration and channelInactive events through the parent channels EventLoop. Result: Stream events are now in the proper order. Fixes #8018. --- .../codec/http2/Http2MultiplexCodec.java | 77 ++++++++++++++++--- .../codec/http2/Http2MultiplexCodecTest.java | 49 ++++++++++++ 2 files changed, 117 insertions(+), 9 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java index 5f17c7503f9f..56c369d0f646 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java @@ -43,11 +43,14 @@ import io.netty.util.internal.StringUtil; import io.netty.util.internal.ThrowableUtil; import io.netty.util.internal.UnstableApi; +import io.netty.util.internal.logging.InternalLogger; +import io.netty.util.internal.logging.InternalLoggerFactory; import java.net.SocketAddress; import java.nio.channels.ClosedChannelException; import java.util.ArrayDeque; import java.util.Queue; +import java.util.concurrent.RejectedExecutionException; import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_STREAM_ID; import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid; @@ -104,6 +107,8 @@ @UnstableApi public class Http2MultiplexCodec extends Http2FrameCodec { + private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultHttp2StreamChannel.class); + private static final ChannelFutureListener CHILD_CHANNEL_REGISTRATION_LISTENER = new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { @@ -899,6 +904,8 @@ public void operationComplete(ChannelFuture future) throws Exception { closePending = false; fireChannelReadPending = false; + final boolean wasActive = isActive(); + // Only ever send a reset frame if the connection is still alive as otherwise it makes no sense at // all anyway. if (parent().isActive() && !streamClosedWithoutError && isStreamIdValid(stream().id())) { @@ -922,10 +929,7 @@ public void operationComplete(ChannelFuture future) throws Exception { closePromise.setSuccess(); promise.setSuccess(); - pipeline().fireChannelInactive(); - if (isRegistered()) { - deregister(unsafe().voidPromise()); - } + fireChannelInactiveAndDeregister(voidPromise(), wasActive); } @Override @@ -935,15 +939,70 @@ public void closeForcibly() { @Override public void deregister(ChannelPromise promise) { + fireChannelInactiveAndDeregister(promise, false); + } + + private void fireChannelInactiveAndDeregister(final ChannelPromise promise, + final boolean fireChannelInactive) { if (!promise.setUncancellable()) { return; } - if (registered) { - registered = true; + + if (!registered) { promise.setSuccess(); - pipeline().fireChannelUnregistered(); - } else { - promise.setFailure(new IllegalStateException("Not registered")); + return; + } + + // As a user may call deregister() from within any method while doing processing in the ChannelPipeline, + // we need to ensure we do the actual deregister operation later. This is necessary to preserve the + // behavior of the AbstractChannel, which always invokes channelUnregistered and channelInactive + // events 'later' to ensure the current events in the handler are completed before these events. + // + // See: + // https://github.com/netty/netty/issues/4435 + invokeLater(new Runnable() { + @Override + public void run() { + if (fireChannelInactive) { + pipeline.fireChannelInactive(); + } + // Some transports like local and AIO does not allow the deregistration of + // an open channel. Their doDeregister() calls close(). Consequently, + // close() calls deregister() again - no need to fire channelUnregistered, so check + // if it was registered. + if (registered) { + registered = false; + pipeline.fireChannelUnregistered(); + } else { + promise.setFailure(new IllegalStateException("Not registered")); + } + safeSetSuccess(promise); + } + }); + } + + private void safeSetSuccess(ChannelPromise promise) { + if (!(promise instanceof VoidChannelPromise) && !promise.trySuccess()) { + logger.warn("Failed to mark a promise as success because it is done already: {}", promise); + } + } + + private void invokeLater(Runnable task) { + try { + // This method is used by outbound operation implementations to trigger an inbound event later. + // They do not trigger an inbound event immediately because an outbound operation might have been + // triggered by another inbound event handler method. If fired immediately, the call stack + // will look like this for example: + // + // handlerA.inboundBufferUpdated() - (1) an inbound handler method closes a connection. + // -> handlerA.ctx.close() + // -> channel.unsafe.close() + // -> handlerA.channelInactive() - (2) another inbound handler method called while in (1) yet + // + // which means the execution of two inbound handler methods of the same handler overlap undesirably. + eventLoop().execute(task); + } catch (RejectedExecutionException e) { + logger.warn("Can't invoke task later as EventLoop rejected it", e); } } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java index 9e455909c95d..c272b9a89859 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java @@ -34,6 +34,7 @@ import java.net.InetSocketAddress; import java.nio.channels.ClosedChannelException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; import org.junit.Before; @@ -583,6 +584,54 @@ public void channelInactive(ChannelHandlerContext ctx) throws Exception { assertFalse(channelActive.get()); } + @Test + public void channelInactiveHappensAfterExceptionCaughtEvents() throws Exception { + final AtomicInteger count = new AtomicInteger(0); + final AtomicInteger exceptionCaught = new AtomicInteger(-1); + final AtomicInteger channelInactive = new AtomicInteger(-1); + final AtomicInteger channelUnregistered = new AtomicInteger(-1); + Http2StreamChannel childChannel = newOutboundStream(); + + childChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() { + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + ctx.close(); + throw new Exception("exception"); + } + }); + + childChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() { + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + channelInactive.set(count.getAndIncrement()); + super.channelInactive(ctx); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + exceptionCaught.set(count.getAndIncrement()); + super.exceptionCaught(ctx, cause); + } + + @Override + public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { + channelUnregistered.set(count.getAndIncrement()); + super.channelUnregistered(ctx); + } + }); + + childChannel.pipeline().fireUserEventTriggered(new Object()); + parentChannel.runPendingTasks(); + + // The events should have happened in this order because the inactive and deregistration events + // get deferred as they do in the AbstractChannel. + assertEquals(0, exceptionCaught.get()); + assertEquals(1, channelInactive.get()); + assertEquals(2, channelUnregistered.get()); + } + @Ignore("not supported anymore atm") @Test public void cancellingWritesBeforeFlush() { From 12f6500a4fdf39f88cc1d5bd4649926a22c7e170 Mon Sep 17 00:00:00 2001 From: Scott Mitchell Date: Fri, 15 Jun 2018 01:28:50 -0700 Subject: [PATCH 046/417] Epoll and Kqueue shouldn't read by default (#8024) Motivation: Epoll and Kqueue channels have internal state which forces a single read operation after channel construction. This violates the Channel#read() interface which indicates that data shouldn't be delivered until this method is called. The behavior is also inconsistent with the NIO transport. Modifications: - Epoll and Kqueue shouldn't unconditionally read upon initialization, and instead should rely upon Channel#read() or auto_read. Result: Epoll and Kqueue are more consistent with NIO. --- .../SocketDataReadInitialStateTest.java | 184 ++++++++++++++++++ .../channel/epoll/AbstractEpollChannel.java | 19 +- .../epoll/AbstractEpollServerChannel.java | 2 +- .../epoll/AbstractEpollStreamChannel.java | 6 +- .../channel/epoll/EpollDatagramChannel.java | 4 +- ...lDomainSocketDataReadInitialStateTest.java | 36 ++++ ...EpollETSocketDataReadInitialStateTest.java | 39 ++++ ...EpollLTSocketDataReadInitialStateTest.java | 39 ++++ .../channel/kqueue/AbstractKQueueChannel.java | 5 +- ...eDomainSocketDataReadInitialStateTest.java | 36 ++++ ...QueueETSocketDataReadInitialStateTest.java | 30 +++ 11 files changed, 378 insertions(+), 22 deletions(-) create mode 100644 testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketDataReadInitialStateTest.java create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketDataReadInitialStateTest.java create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollETSocketDataReadInitialStateTest.java create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollLTSocketDataReadInitialStateTest.java create mode 100644 transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketDataReadInitialStateTest.java create mode 100644 transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueETSocketDataReadInitialStateTest.java diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketDataReadInitialStateTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketDataReadInitialStateTest.java new file mode 100644 index 000000000000..f4d218d957f9 --- /dev/null +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketDataReadInitialStateTest.java @@ -0,0 +1,184 @@ +/* + * Copyright 2018 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: + * + * http://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.testsuite.transport.socket; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.SimpleChannelInboundHandler; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +import static io.netty.channel.ChannelOption.AUTO_READ; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class SocketDataReadInitialStateTest extends AbstractSocketTest { + @Test(timeout = 10000) + public void testAutoReadOffNoDataReadUntilReadCalled() throws Throwable { + run(); + } + + public void testAutoReadOffNoDataReadUntilReadCalled(ServerBootstrap sb, Bootstrap cb) throws Throwable { + Channel serverChannel = null; + Channel clientChannel = null; + final int sleepMs = 100; + try { + sb.option(AUTO_READ, false); + sb.childOption(AUTO_READ, false); + cb.option(AUTO_READ, false); + final CountDownLatch serverReadyLatch = new CountDownLatch(1); + final CountDownLatch acceptorReadLatch = new CountDownLatch(1); + final CountDownLatch serverReadLatch = new CountDownLatch(1); + final CountDownLatch clientReadLatch = new CountDownLatch(1); + final AtomicReference serverConnectedChannelRef = new AtomicReference(); + + sb.handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + acceptorReadLatch.countDown(); + ctx.fireChannelRead(msg); + } + }); + } + }); + + sb.childHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + serverConnectedChannelRef.set(ch); + ch.pipeline().addLast(new SimpleChannelInboundHandler() { + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) { + ctx.writeAndFlush(msg.retainedDuplicate()); + serverReadLatch.countDown(); + } + }); + serverReadyLatch.countDown(); + } + }); + + cb.handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addLast(new SimpleChannelInboundHandler() { + @Override + protected void channelRead0(ChannelHandlerContext ctx, Object msg) { + clientReadLatch.countDown(); + } + }); + } + }); + + serverChannel = sb.bind().sync().channel(); + clientChannel = cb.connect(serverChannel.localAddress()).sync().channel(); + clientChannel.writeAndFlush(clientChannel.alloc().buffer().writeZero(1)).syncUninterruptibly(); + + // The acceptor shouldn't read any data until we call read() below, but give it some time to see if it will. + Thread.sleep(sleepMs); + assertEquals(1, acceptorReadLatch.getCount()); + serverChannel.read(); + serverReadyLatch.await(); + + Channel serverConnectedChannel = serverConnectedChannelRef.get(); + assertNotNull(serverConnectedChannel); + + // Allow some amount of time for the server peer to receive the message (which isn't expected to happen + // until we call read() below). + Thread.sleep(sleepMs); + assertEquals(1, serverReadLatch.getCount()); + serverConnectedChannel.read(); + serverReadLatch.await(); + + // Allow some amount of time for the client to read the echo. + Thread.sleep(sleepMs); + assertEquals(1, clientReadLatch.getCount()); + clientChannel.read(); + clientReadLatch.await(); + } finally { + if (serverChannel != null) { + serverChannel.close().sync(); + } + if (clientChannel != null) { + clientChannel.close().sync(); + } + } + } + + @Test(timeout = 10000) + public void testAutoReadOnDataReadImmediately() throws Throwable { + run(); + } + + public void testAutoReadOnDataReadImmediately(ServerBootstrap sb, Bootstrap cb) throws Throwable { + Channel serverChannel = null; + Channel clientChannel = null; + try { + sb.option(AUTO_READ, true); + sb.childOption(AUTO_READ, true); + cb.option(AUTO_READ, true); + final CountDownLatch serverReadLatch = new CountDownLatch(1); + final CountDownLatch clientReadLatch = new CountDownLatch(1); + + sb.childHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addLast(new SimpleChannelInboundHandler() { + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) { + ctx.writeAndFlush(msg.retainedDuplicate()); + serverReadLatch.countDown(); + } + }); + } + }); + + cb.handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addLast(new SimpleChannelInboundHandler() { + @Override + protected void channelRead0(ChannelHandlerContext ctx, Object msg) { + clientReadLatch.countDown(); + } + }); + } + }); + + serverChannel = sb.bind().sync().channel(); + clientChannel = cb.connect(serverChannel.localAddress()).sync().channel(); + clientChannel.writeAndFlush(clientChannel.alloc().buffer().writeZero(1)).syncUninterruptibly(); + serverReadLatch.await(); + clientReadLatch.await(); + } finally { + if (serverChannel != null) { + serverChannel.close().sync(); + } + if (clientChannel != null) { + clientChannel.close().sync(); + } + } + } +} diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java index 03574b9715cb..25ae95b2d4d2 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java @@ -60,7 +60,6 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann private static final ClosedChannelException DO_CLOSE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace( new ClosedChannelException(), AbstractEpollChannel.class, "doClose()"); private static final ChannelMetadata METADATA = new ChannelMetadata(false); - private final int readFlag; final LinuxSocket socket; /** * The future of the current connection attempt. If not null, subsequent @@ -79,15 +78,13 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann protected volatile boolean active; - AbstractEpollChannel(LinuxSocket fd, int flag) { - this(null, fd, flag, false); + AbstractEpollChannel(LinuxSocket fd) { + this(null, fd, false); } - AbstractEpollChannel(Channel parent, LinuxSocket fd, int flag, boolean active) { + AbstractEpollChannel(Channel parent, LinuxSocket fd, boolean active) { super(parent); socket = checkNotNull(fd, "fd"); - readFlag = flag; - flags |= flag; this.active = active; if (active) { // Directly cache the remote and local addresses @@ -97,11 +94,9 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann } } - AbstractEpollChannel(Channel parent, LinuxSocket fd, int flag, SocketAddress remote) { + AbstractEpollChannel(Channel parent, LinuxSocket fd, SocketAddress remote) { super(parent); socket = checkNotNull(fd, "fd"); - readFlag = flag; - flags |= flag; active = true; // Directly cache the remote and local addresses // See https://github.com/netty/netty/issues/2359 @@ -228,7 +223,7 @@ protected final void doBeginRead() throws Exception { // We must set the read flag here as it is possible the user didn't read in the last read loop, the // executeEpollInReadyRunnable could read nothing, and if the user doesn't explicitly call read they will // never get data after this. - setFlag(readFlag); + setFlag(Native.EPOLLIN); // If EPOLL ET mode is enabled and auto read was toggled off on the last read loop then we may not be notified // again if we didn't consume all the data. So we force a read operation here if there maybe more data. @@ -268,7 +263,7 @@ public void run() { } else { // The EventLoop is not registered atm so just update the flags so the correct value // will be used once the channel is registered - flags &= ~readFlag; + flags &= ~Native.EPOLLIN; } } @@ -535,7 +530,7 @@ protected final void clearEpollIn0() { assert eventLoop().inEventLoop(); try { readPending = false; - clearFlag(readFlag); + clearFlag(Native.EPOLLIN); } catch (IOException e) { // When this happens there is something completely wrong with either the filedescriptor or epoll, // so fire the exception through the pipeline and close the Channel. diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java index ebda9ebfe783..b7ccf37b2e36 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java @@ -39,7 +39,7 @@ protected AbstractEpollServerChannel(int fd) { } AbstractEpollServerChannel(LinuxSocket fd, boolean active) { - super(null, fd, Native.EPOLLIN, active); + super(null, fd, active); } @Override diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java index 7bb9e595bfd9..ea871db3e193 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java @@ -99,19 +99,19 @@ protected AbstractEpollStreamChannel(int fd) { } AbstractEpollStreamChannel(Channel parent, LinuxSocket fd) { - super(parent, fd, Native.EPOLLIN, true); + super(parent, fd, true); // Add EPOLLRDHUP so we are notified once the remote peer close the connection. flags |= Native.EPOLLRDHUP; } AbstractEpollStreamChannel(Channel parent, LinuxSocket fd, SocketAddress remote) { - super(parent, fd, Native.EPOLLIN, remote); + super(parent, fd, remote); // Add EPOLLRDHUP so we are notified once the remote peer close the connection. flags |= Native.EPOLLRDHUP; } protected AbstractEpollStreamChannel(LinuxSocket fd, boolean active) { - super(null, fd, Native.EPOLLIN, active); + super(null, fd, active); // Add EPOLLRDHUP so we are notified once the remote peer close the connection. flags |= Native.EPOLLRDHUP; } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java index a19191d5967e..d9f48464f562 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java @@ -59,7 +59,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements private volatile boolean connected; public EpollDatagramChannel() { - super(newSocketDgram(), Native.EPOLLIN); + super(newSocketDgram()); config = new EpollDatagramChannelConfig(this); } @@ -68,7 +68,7 @@ public EpollDatagramChannel(int fd) { } EpollDatagramChannel(LinuxSocket fd) { - super(null, fd, Native.EPOLLIN, true); + super(null, fd, true); config = new EpollDatagramChannelConfig(this); } diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketDataReadInitialStateTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketDataReadInitialStateTest.java new file mode 100644 index 000000000000..9f848cd18d2d --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketDataReadInitialStateTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.epoll; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.SocketDataReadInitialStateTest; + +import java.net.SocketAddress; +import java.util.List; + +public class EpollDomainSocketDataReadInitialStateTest extends SocketDataReadInitialStateTest { + @Override + protected SocketAddress newSocketAddress() { + return EpollSocketTestPermutation.newSocketAddress(); + } + + @Override + protected List> newFactories() { + return EpollSocketTestPermutation.INSTANCE.domainSocket(); + } +} diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollETSocketDataReadInitialStateTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollETSocketDataReadInitialStateTest.java new file mode 100644 index 000000000000..9c27f2503074 --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollETSocketDataReadInitialStateTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.epoll; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBufAllocator; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.SocketDataReadInitialStateTest; + +import java.util.List; + +public class EpollETSocketDataReadInitialStateTest extends SocketDataReadInitialStateTest { + @Override + protected List> newFactories() { + return EpollSocketTestPermutation.INSTANCE.socket(); + } + + @Override + protected void configure(ServerBootstrap bootstrap, Bootstrap bootstrap2, ByteBufAllocator allocator) { + super.configure(bootstrap, bootstrap2, allocator); + bootstrap.option(EpollChannelOption.EPOLL_MODE, EpollMode.EDGE_TRIGGERED) + .childOption(EpollChannelOption.EPOLL_MODE, EpollMode.EDGE_TRIGGERED); + bootstrap2.option(EpollChannelOption.EPOLL_MODE, EpollMode.EDGE_TRIGGERED); + } +} diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollLTSocketDataReadInitialStateTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollLTSocketDataReadInitialStateTest.java new file mode 100644 index 000000000000..78614edd3586 --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollLTSocketDataReadInitialStateTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.epoll; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBufAllocator; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.SocketDataReadInitialStateTest; + +import java.util.List; + +public class EpollLTSocketDataReadInitialStateTest extends SocketDataReadInitialStateTest { + @Override + protected List> newFactories() { + return EpollSocketTestPermutation.INSTANCE.socket(); + } + + @Override + protected void configure(ServerBootstrap bootstrap, Bootstrap bootstrap2, ByteBufAllocator allocator) { + super.configure(bootstrap, bootstrap2, allocator); + bootstrap.option(EpollChannelOption.EPOLL_MODE, EpollMode.LEVEL_TRIGGERED) + .childOption(EpollChannelOption.EPOLL_MODE, EpollMode.LEVEL_TRIGGERED); + bootstrap2.option(EpollChannelOption.EPOLL_MODE, EpollMode.LEVEL_TRIGGERED); + } +} diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java index 7ca2bd9f1c74..6073fa9ca120 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java @@ -65,7 +65,7 @@ abstract class AbstractKQueueChannel extends AbstractChannel implements UnixChan private SocketAddress requestedRemoteAddress; final BsdSocket socket; - private boolean readFilterEnabled = true; + private boolean readFilterEnabled; private boolean writeFilterEnabled; boolean readReadyRunnablePending; boolean inputClosedSeenErrorOnRead; @@ -187,9 +187,6 @@ protected void doDeregister() throws Exception { evSet0(Native.EVFILT_SOCK, Native.EV_DELETE, 0); ((KQueueEventLoop) eventLoop()).remove(this); - - // Set the filters back to the initial state in case this channel is registered with another event loop. - readFilterEnabled = true; } @Override diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketDataReadInitialStateTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketDataReadInitialStateTest.java new file mode 100644 index 000000000000..10e538b8e42d --- /dev/null +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketDataReadInitialStateTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.kqueue; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.SocketDataReadInitialStateTest; + +import java.net.SocketAddress; +import java.util.List; + +public class KQueueDomainSocketDataReadInitialStateTest extends SocketDataReadInitialStateTest { + @Override + protected SocketAddress newSocketAddress() { + return KQueueSocketTestPermutation.newSocketAddress(); + } + + @Override + protected List> newFactories() { + return KQueueSocketTestPermutation.INSTANCE.domainSocket(); + } +} diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueETSocketDataReadInitialStateTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueETSocketDataReadInitialStateTest.java new file mode 100644 index 000000000000..3055b1dafe68 --- /dev/null +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueETSocketDataReadInitialStateTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.kqueue; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.SocketDataReadInitialStateTest; + +import java.util.List; + +public class KQueueETSocketDataReadInitialStateTest extends SocketDataReadInitialStateTest { + @Override + protected List> newFactories() { + return KQueueSocketTestPermutation.INSTANCE.socket(); + } +} From 9b0fa2f668e8abe2e6d883d4edaed1422e81abe0 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 15 Jun 2018 13:24:42 +0200 Subject: [PATCH 047/417] Update to netty-tcnative 2.0.9.Final which fixes a memory leak (#8026) Motivation: netty-tcnative 2.0.9.Final was released which fixes a memory leak that can happen if client auth is used via client side. Modifications: Update to latest netty-tcnative. Result: No more memory leak. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 71e3e4b2da75..501475e21fd1 100644 --- a/pom.xml +++ b/pom.xml @@ -221,7 +221,7 @@ fedora netty-tcnative - 2.0.8.Final + 2.0.9.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber From a8549b138d2dcf64539fe4707c18fb1a3003e06f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 17 Jun 2018 10:55:43 +0200 Subject: [PATCH 048/417] Update to netty-tcnative 2.0.10.Final as 2.0.9.Final did not contain all native libs for boringssl. (#8031) Motivation: netty-tcnative 2.0.9 did not contain all native code for boringssl due a release mistake. Modifications: Update to 2.0.10 Result: Use latest netty-tcnative release. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 501475e21fd1..de59d8e71262 100644 --- a/pom.xml +++ b/pom.xml @@ -221,7 +221,7 @@ fedora netty-tcnative - 2.0.9.Final + 2.0.10.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber From 2d470059011616502895005d4ba55545373b9b15 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 18 Jun 2018 20:31:15 +0200 Subject: [PATCH 049/417] Use java 11+ea18 (#8034) Motivation: Java 11+ea18 was released. Modifications: Update to latest version. Result: Testing with latest java 11 release. --- docker/docker-compose.centos-6.111.yaml | 2 +- docker/docker-compose.centos-7.111.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml index b4d2dcba9769..e11c2d87f167 100644 --- a/docker/docker-compose.centos-6.111.yaml +++ b/docker/docker-compose.centos-6.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "1.11.0-16" + java_version : "1.11.0-18" test: image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-7.111.yaml b/docker/docker-compose.centos-7.111.yaml index 6205e3988614..4a1d1eea2dfb 100644 --- a/docker/docker-compose.centos-7.111.yaml +++ b/docker/docker-compose.centos-7.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "1.11.0-16" + java_version : "1.11.0-18" test: image: netty:centos-7-1.11 From 370de7c44652fe461067e1ac64c38a1014584ae0 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 19 Jun 2018 08:11:10 +0200 Subject: [PATCH 050/417] Revert "Ignore some test-flakiness when using Java11+ due outstanding Java11 bug. (#7984)" (#8035) Motivation: This reverts commit 4b728cd5bc53195bced516f33a1ea0a0def5604e as it was fixes in Java 11 ea+17. Modification: Revert previous added workaround as this is fixed in Java 11 now. Result: No more workaround for test included. --- .../socket/SocketHalfClosedTest.java | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java index 3576414e0911..cd03ac3c9be7 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java @@ -33,9 +33,7 @@ import io.netty.channel.socket.ChannelInputShutdownReadComplete; import io.netty.channel.socket.ChannelOutputShutdownEvent; import io.netty.channel.socket.DuplexChannel; -import io.netty.channel.socket.oio.OioSocketChannel; import io.netty.util.UncheckedBooleanSupplier; -import io.netty.util.internal.PlatformDependent; import org.junit.Test; import java.util.concurrent.CountDownLatch; @@ -416,26 +414,19 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { @Override public void channelInactive(ChannelHandlerContext ctx) { - checkPrematureClose(ctx); + checkPrematureClose(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { ctx.close(); - checkPrematureClose(ctx); + checkPrematureClose(); } - private void checkPrematureClose(ChannelHandlerContext ctx) { + private void checkPrematureClose() { if (bytesRead < expectedBytes || !seenOutputShutdown) { - if (ctx.channel() instanceof OioSocketChannel && seenOutputShutdown - && PlatformDependent.javaVersion() >= 11) { - // If we are using OIO and are using Java11 this is expected atm. - // See http://mail.openjdk.java.net/pipermail/net-dev/2018-May/011511.html. - doneLatch.countDown(); - } else { - causeRef.set(new IllegalStateException("leader premature close")); - doneLatch.countDown(); - } + causeRef.set(new IllegalStateException("leader premature close")); + doneLatch.countDown(); } } } From 3fb1b992efd24aece49b3d80e96ed43a1bf44e9c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 19 Jun 2018 08:12:02 +0200 Subject: [PATCH 051/417] Remove some cipher protocol combos for tests due removal in more recent versions of OpenSSL (#8033) Motivation: Some of the cipher protocol combos that were used are no longer included in more recent OpenSSL releases. Modifications: Remove some combos that were used for testing. Result: Tests also pass in more recent OpenSSL versions (1.1.0+). --- .../netty/handler/ssl/OpenSslEngineTest.java | 45 +------------------ 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index ec2d4bc33bcc..55864910d8d0 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -479,35 +479,23 @@ public void testWrapWithDifferentSizesTLSv1() throws Exception { .sslProvider(sslServerProvider()) .build(); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ADH-AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ECDHE-RSA-AES128-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ADH-CAMELLIA128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "DES-CBC3-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "AECDH-AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "AECDH-DES-CBC3-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "CAMELLIA128-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "DHE-RSA-AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "SEED-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "RC4-MD5"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ADH-AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "AES256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ADH-SEED-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ADH-DES-CBC3-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "EDH-RSA-DES-CBC3-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ADH-RC4-MD5"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "IDEA-CBC-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "DHE-RSA-AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "RC4-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "CAMELLIA256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "AECDH-RC4-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "DHE-RSA-SEED-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "AECDH-AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ECDHE-RSA-DES-CBC3-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ADH-CAMELLIA256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "DHE-RSA-CAMELLIA256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ECDHE-RSA-AES256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "DHE-RSA-CAMELLIA128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ECDHE-RSA-RC4-SHA"); } @@ -523,18 +511,9 @@ public void testWrapWithDifferentSizesTLSv1_1() throws Exception { .build(); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "ECDHE-RSA-AES256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "DHE-RSA-AES256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "DHE-RSA-CAMELLIA256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "ADH-CAMELLIA256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "ADH-AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "CAMELLIA256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "AECDH-AES128-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "DHE-RSA-CAMELLIA128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "ECDHE-RSA-AES256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "ADH-AES128-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "ADH-SEED-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "ADH-CAMELLIA128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "SEED-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "CAMELLIA128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "IDEA-CBC-SHA"); @@ -559,15 +538,10 @@ public void testWrapWithDifferentSizesTLSv1_2() throws Exception { .sslProvider(sslServerProvider()) .build(); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ADH-AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES128-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ADH-CAMELLIA128-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "DHE-RSA-AES256-GCM-SHA384"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "DES-CBC3-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "AECDH-AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "AES128-GCM-SHA256"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "DHE-RSA-AES128-GCM-SHA256"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES256-SHA384"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "AECDH-DES-CBC3-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "AES256-GCM-SHA384"); @@ -575,35 +549,19 @@ public void testWrapWithDifferentSizesTLSv1_2() throws Exception { testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES128-GCM-SHA256"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES128-SHA256"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "CAMELLIA128-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "DHE-RSA-AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "SEED-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "RC4-MD5"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ADH-AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "AES256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ADH-SEED-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "DHE-RSA-AES128-SHA256"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ADH-DES-CBC3-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "EDH-RSA-DES-CBC3-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ADH-RC4-MD5"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "IDEA-CBC-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "DHE-RSA-AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "RC4-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "CAMELLIA256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ADH-AES128-GCM-SHA256"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "AES128-SHA256"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "AECDH-RC4-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ADH-AES256-GCM-SHA384"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "DHE-RSA-SEED-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "DHE-RSA-AES256-SHA256"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "AECDH-AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ECDHE-RSA-DES-CBC3-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ADH-CAMELLIA256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES256-GCM-SHA384"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "DHE-RSA-CAMELLIA256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ADH-AES256-SHA256"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ADH-AES128-SHA256"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES256-SHA"); - testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "DHE-RSA-CAMELLIA128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_2, "ECDHE-RSA-RC4-SHA"); } @@ -981,7 +939,8 @@ private void testWrapWithDifferentSizes(String protocol, String cipher) throws E try { handshake(clientEngine, serverEngine); } catch (SSLException e) { - if (e.getMessage().contains("unsupported protocol")) { + if (e.getMessage().contains("unsupported protocol") || + e.getMessage().contains("no protocols available")) { Assume.assumeNoException(protocol + " not supported with cipher " + cipher, e); } throw e; From b1d4b2af1c189666963a3545121cbc5477e3c394 Mon Sep 17 00:00:00 2001 From: radai-rosenblatt Date: Tue, 19 Jun 2018 22:00:39 -0700 Subject: [PATCH 052/417] fix grammar in javadoc Signed-off-by: radai-rosenblatt --- .../src/main/java/io/netty/bootstrap/AbstractBootstrap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/src/main/java/io/netty/bootstrap/AbstractBootstrap.java b/transport/src/main/java/io/netty/bootstrap/AbstractBootstrap.java index fb05c8b263e9..1030c320411e 100644 --- a/transport/src/main/java/io/netty/bootstrap/AbstractBootstrap.java +++ b/transport/src/main/java/io/netty/bootstrap/AbstractBootstrap.java @@ -126,7 +126,7 @@ public B channelFactory(ChannelFactory channelFactory) { * {@link io.netty.channel.ChannelFactory} which is used to create {@link Channel} instances from * when calling {@link #bind()}. This method is usually only used if {@link #channel(Class)} * is not working for you because of some more complex needs. If your {@link Channel} implementation - * has a no-args constructor, its highly recommend to just use {@link #channel(Class)} for + * has a no-args constructor, its highly recommend to just use {@link #channel(Class)} to * simplify your code. */ @SuppressWarnings({ "unchecked", "deprecation" }) From 9b95b8ee628983e3e4434da93fffb893edff4aa2 Mon Sep 17 00:00:00 2001 From: nickhill Date: Wed, 20 Jun 2018 00:13:09 -0700 Subject: [PATCH 053/417] Reduce array allocations during CompositeByteBuf construction Motivation: Eliminate avoidable backing array reallocations when constructing composite ByteBufs from existing buffer arrays/Iterables. This also applies to the Unpooled.wrappedBuffer(...) methods. Modifications: Ensure the initial components ComponentList is sized at least as large as the provided buffer array/Iterable in the CompositeByteBuffer constructors. In single-arg Unpooled.wrappedBuffer(...) methods, set maxNumComponents to the count of provided buffers, rather than a fixed default of 16. It seems likely that most usage of these involves wrapping a list without subsequent modification, particularly since they return a ByteBuf rather than CompositeByteBuf. If a different/larger max is required there are already the wrappedBuffer(int, ...) variants. In fact the current behaviour could be considered inconsistent - if you call Unpooled.wrappedBuffer(int, ByteBuf) with a single buffer, you might expect to subsequently be able to add buffers to it (since you specified a max related to consolidation), but it will in fact return just a slice of the provided ByteBuf. Result: Fewer and smaller allocations in some cases when using CompositeByteBufs or Unpooled.wrappedBuffer(...). --- .../main/java/io/netty/buffer/CompositeByteBuf.java | 13 ++++++++----- buffer/src/main/java/io/netty/buffer/Unpooled.java | 6 +++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 7afff7e7f13e..bcd5f0a56483 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -61,7 +61,7 @@ public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumCompon this.alloc = alloc; this.direct = direct; this.maxNumComponents = maxNumComponents; - components = newList(maxNumComponents); + components = newList(0, maxNumComponents); } public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf... buffers) { @@ -82,7 +82,7 @@ public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumCompon this.alloc = alloc; this.direct = direct; this.maxNumComponents = maxNumComponents; - components = newList(maxNumComponents); + components = newList(len, maxNumComponents); addComponents0(false, 0, buffers, offset, len); consolidateIfNeeded(); @@ -100,18 +100,21 @@ public CompositeByteBuf( "maxNumComponents: " + maxNumComponents + " (expected: >= 2)"); } + int len = buffers instanceof Collection ? ((Collection) buffers).size() : 0; + this.alloc = alloc; this.direct = direct; this.maxNumComponents = maxNumComponents; - components = newList(maxNumComponents); + components = newList(len, maxNumComponents); addComponents0(false, 0, buffers); consolidateIfNeeded(); setIndex(0, capacity()); } - private static ComponentList newList(int maxNumComponents) { - return new ComponentList(Math.min(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, maxNumComponents)); + private static ComponentList newList(int initComponents, int maxNumComponents) { + int capacityGuess = Math.min(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, maxNumComponents); + return new ComponentList(Math.max(initComponents, capacityGuess)); } // Special constructor used by WrappedCompositeByteBuf diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/netty/buffer/Unpooled.java index 3639a1776848..87559897826e 100644 --- a/buffer/src/main/java/io/netty/buffer/Unpooled.java +++ b/buffer/src/main/java/io/netty/buffer/Unpooled.java @@ -238,7 +238,7 @@ public static ByteBuf wrappedBuffer(ByteBuf buffer) { * content will be visible to the returned buffer. */ public static ByteBuf wrappedBuffer(byte[]... arrays) { - return wrappedBuffer(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, arrays); + return wrappedBuffer(arrays.length, arrays); } /** @@ -249,7 +249,7 @@ public static ByteBuf wrappedBuffer(byte[]... arrays) { * @return The readable portion of the {@code buffers}. The caller is responsible for releasing this buffer. */ public static ByteBuf wrappedBuffer(ByteBuf... buffers) { - return wrappedBuffer(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, buffers); + return wrappedBuffer(buffers.length, buffers); } /** @@ -258,7 +258,7 @@ public static ByteBuf wrappedBuffer(ByteBuf... buffers) { * specified buffers will be visible to the returned buffer. */ public static ByteBuf wrappedBuffer(ByteBuffer... buffers) { - return wrappedBuffer(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, buffers); + return wrappedBuffer(buffers.length, buffers); } /** From 4c709be1abf6e52c6a5640c1672d259f1de638d1 Mon Sep 17 00:00:00 2001 From: Stefan Lance Date: Wed, 20 Jun 2018 10:24:29 -0700 Subject: [PATCH 054/417] Print correct invalid character after unwrapping value in CookieEncoder Motivation: If a wrapped cookie value with an invalid charcater is passed to the strict encoder, an exception is thrown on validation but the error message contains a character at the wrong position. Modifications: Print `unwrappedValue.charAt(pos)` instead of `value.charAt(pos)`. Result: The exception indicates the correct invalid character in the unwrapped cookie. --- .../handler/codec/http/cookie/CookieEncoder.java | 3 ++- .../codec/http/cookie/ServerCookieEncoderTest.java | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieEncoder.java index d64876815295..39d1d07ccba2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieEncoder.java @@ -44,7 +44,8 @@ protected void validateCookie(String name, String value) { } if ((pos = firstInvalidCookieValueOctet(unwrappedValue)) >= 0) { - throw new IllegalArgumentException("Cookie value contains an invalid char: " + value.charAt(pos)); + throw new IllegalArgumentException("Cookie value contains an invalid char: " + + unwrappedValue.charAt(pos)); } } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/cookie/ServerCookieEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/cookie/ServerCookieEncoderTest.java index 723a7a17a9f6..82f813ff4779 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/cookie/ServerCookieEncoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/cookie/ServerCookieEncoderTest.java @@ -18,7 +18,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.junit.matchers.JUnitMatchers.containsString; import io.netty.handler.codec.DateFormatter; import java.text.ParseException; @@ -131,6 +133,15 @@ public void illegalCharInCookieValueMakesStrictEncoderThrowsException() { assertEquals(illegalChars.size(), exceptions); } + @Test + public void illegalCharInWrappedValueAppearsInException() { + try { + ServerCookieEncoder.STRICT.encode(new DefaultCookie("name", "\"value,\"")); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage().toLowerCase(), containsString("cookie value contains an invalid char: ,")); + } + } + @Test public void testEncodingMultipleCookiesLax() { List result = new ArrayList(); From 8687e1eeedeba1bc4b639602f439f4f9d0f366db Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Sat, 16 Jun 2018 08:32:27 -0600 Subject: [PATCH 055/417] Don't fail the deregistration promise in Http2MultiplexCodec Motivation: We deviate from the AbstractChannel implementation on deregistration by failing the provided promise if the channel is already deregistered. In contrast, AbstractChannel will always set the promise to successfully done. Modification: Change the Http2MultiplexCodec.DefaultHttp2StreamChannel.Http2ChannelUnsafe to always set the promise provided to deregister as done as is the case in AbstractChannel. --- .../io/netty/handler/codec/http2/Http2MultiplexCodec.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java index 56c369d0f646..33b59da13e12 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java @@ -966,15 +966,11 @@ public void run() { if (fireChannelInactive) { pipeline.fireChannelInactive(); } - // Some transports like local and AIO does not allow the deregistration of - // an open channel. Their doDeregister() calls close(). Consequently, - // close() calls deregister() again - no need to fire channelUnregistered, so check - // if it was registered. + // The user can fire `deregister` events multiple times but we only want to fire the pipeline + // event if the channel was actually registered. if (registered) { registered = false; pipeline.fireChannelUnregistered(); - } else { - promise.setFailure(new IllegalStateException("Not registered")); } safeSetSuccess(promise); } From cb420a9ffcad4038df862324d1594c401389bda5 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 5 Jun 2018 10:43:21 +0200 Subject: [PATCH 056/417] Including the setup code in the benchmark method to avoid JMH Invocation level hiccups. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: The usage of Invocation level for JMH fixture methods (setup/teardown) inccurs in a significant impact in in the benchmark time (see org.openjdk.jmh.annotations.Level documentation). When the benchmark and the setup/teardown is too small (less than a milisecond) the Invocation level might saturate the system with timestamp requests and iteration synchronizations which introduce artificial latency, throughput, and scalability bottlenecks. In the HeadersBenchmark, all benchmarks take less than 100ns and the Invocation level setup offsets the measurement considerably. As fixture methods is defined for the entire class, this overhead also impacts every single benchmark in this class, not only the ones that use the emptyHttpHeaders object (cleaned in the setup). The recommended fix patch here is to include the setup/teardown code in the benchmark where the object is used. Modifications: Include the setup/teardown code in the relevant benchmark methods. Remove the setup/teardown method of Invocation level from the benchmark class. Result: We run all benchmarks from HeadersBenchmark 10 times with default parameter, we observe: - Benchmarks that were not directly affected by the fix patch, improved execution time. For instance, http2Remove with (exampleHeader = THREE) had its median reported as 2x faster than the original version. - Benchmarks that had the setup code inserted (eg. http2AddAllFastest) did not suffer a significant punch in the execution time, as the benchmarks are not dominated by the clear(). Environment: Tests run on a Computational server with CPU: E5-1660-3.3GHZ  (6 cores + HT), 64 GB RAM. --- .../netty/microbench/headers/HeadersBenchmark.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java b/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java index 589a89200810..c8706a0a447d 100644 --- a/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java +++ b/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java @@ -102,14 +102,6 @@ public void setup() { emptyHttp2HeadersNoValidate = new DefaultHttp2Headers(false); } - @Setup(Level.Invocation) - public void setupEmptyHeaders() { - emptyHttpHeaders.clear(); - emptyHttp2Headers .clear(); - emptyHttpHeadersNoValidate.clear(); - emptyHttp2HeadersNoValidate.clear(); - } - @Benchmark @BenchmarkMode(Mode.AverageTime) public void httpRemove(Blackhole bh) { @@ -183,30 +175,35 @@ public void http2Iterate(Blackhole bh) { @BenchmarkMode(Mode.AverageTime) public void httpAddAllFastest(Blackhole bh) { bh.consume(emptyHttpHeadersNoValidate.add(httpHeaders)); + emptyHttpHeadersNoValidate.clear(); } @Benchmark @BenchmarkMode(Mode.AverageTime) public void httpAddAllFast(Blackhole bh) { bh.consume(emptyHttpHeaders.add(httpHeaders)); + emptyHttpHeaders.clear(); } @Benchmark @BenchmarkMode(Mode.AverageTime) public void http2AddAllFastest(Blackhole bh) { bh.consume(emptyHttp2HeadersNoValidate.add(http2Headers)); + emptyHttp2HeadersNoValidate.clear(); } @Benchmark @BenchmarkMode(Mode.AverageTime) public void http2AddAllFast(Blackhole bh) { bh.consume(emptyHttp2Headers.add(http2Headers)); + emptyHttp2Headers.clear(); } @Benchmark @BenchmarkMode(Mode.AverageTime) public void http2AddAllSlow(Blackhole bh) { bh.consume(emptyHttp2Headers.add(slowHttp2Headers)); + emptyHttp2Headers.clear(); } private static final class SlowHeaders implements Headers { From 4a8d3a274cbad54daa652d6e9e082b4c1c843f84 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 5 Jun 2018 10:48:26 +0200 Subject: [PATCH 057/417] Including the setup code in the benchmark method to avoid JMH Invocation level hiccups. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: The usage of Invocation level for JMH fixture methods (setup/teardown) inccurs in a significant overhead in the benchmark time (see org.openjdk.jmh.annotations.Level documentation). In the case of CodecInputListBenchmark, benchmarks are far too small (less than 50ns) and the Invocation level setup offsets the measurement considerably. On such cases, the recommended fix patch is to include the setup/teardown code in the benchmark method. Modifications: Include the setup/teardown code in the relevant benchmark methods. Remove the setup/teardown methods from the benchmark class. Result: We run the entire benchmark 10 times with default parameters we observed: - ArrayList benchmark affected directly by JMH overhead is now from 15-80% faster. - CodecList benchmark is now 50% faster than original (even with the setup code being measured). - Recyclable ArrayList is ~30% slower. - All benchmarks have significant different means (ANOVA) and medians (Moore) Mode: Throughput (Higher the better) Method Full params Factor Modified (Median) Original (Median) recyclableArrayList (elements = 1) 0.615520967 21719082.75 35285691.2 recyclableArrayList (elements = 4) 0.699553431 17149442.76 24514843.31 arrayList (elements = 4) 1.152666631 27120407.18 23528404.88 codecOutList (elements = 1) 1.527275908 67251089.04 44033359.47 codecOutList (elements = 4) 1.596917095 59174088.78 37055204.03 arrayList (elements = 1) 1.878616889 62188238.24 33103204.06 Environment: Tests run on a Computational server with CPU: E5-1660-3.3GHZ  (6 cores + HT), 64 GB RAM. --- .../handler/codec/CodecOutputListBenchmark.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/microbench/src/main/java/io/netty/handler/codec/CodecOutputListBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/CodecOutputListBenchmark.java index 8db4a99ffc25..d7d40307a5e0 100644 --- a/microbench/src/main/java/io/netty/handler/codec/CodecOutputListBenchmark.java +++ b/microbench/src/main/java/io/netty/handler/codec/CodecOutputListBenchmark.java @@ -18,10 +18,8 @@ import io.netty.microbench.util.AbstractMicrobenchmark; import io.netty.util.internal.RecyclableArrayList; import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; @@ -39,13 +37,6 @@ public class CodecOutputListBenchmark extends AbstractMicrobenchmark { @Param({ "1", "4" }) public int elements; - @Setup(Level.Invocation) - public void setup() { - codecOutputList = CodecOutputList.newInstance(); - recycleableArrayList = RecyclableArrayList.newInstance(16); - arrayList = new ArrayList(16); - } - @TearDown public void destroy() { codecOutputList.recycle(); @@ -54,16 +45,19 @@ public void destroy() { @Benchmark public void codecOutList() { + codecOutputList = CodecOutputList.newInstance(); benchmarkAddAndClear(codecOutputList, elements); } @Benchmark public void recyclableArrayList() { + recycleableArrayList = RecyclableArrayList.newInstance(16); benchmarkAddAndClear(recycleableArrayList, elements); } @Benchmark public void arrayList() { + arrayList = new ArrayList(16); benchmarkAddAndClear(arrayList, elements); } From 0337ecdcc87ae19b9f329eeffefa336354c1d0a0 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 19 Jun 2018 18:46:13 +0200 Subject: [PATCH 058/417] Allow to cache keymaterial when using OpenSSL Motiviation: During profiling it showed that a lot of time during the handshake is spent by parsing the key / chain over and over again. We should cache these parsed structures if possible to reduce the overhead during handshake. Modification: - Use new APIs provided by https://github.com/netty/netty-tcnative/pull/360. - Introduce OpensslStaticX509KeyManagerFactory which allows to wrap another KeyManagerFactory and caches the key material provided by it. Result: In benchmarks handshake times have improved by 30 %. --- .../OpenSslCachingKeyMaterialProvider.java | 68 ++++++++ .../OpenSslCachingX509KeyManagerFactory.java | 60 +++++++ .../OpenSslExtendedKeyMaterialManager.java | 40 ----- .../netty/handler/ssl/OpenSslKeyMaterial.java | 124 +++++++++++++++ .../ssl/OpenSslKeyMaterialManager.java | 130 +++++---------- .../ssl/OpenSslKeyMaterialProvider.java | 94 +++++++++++ .../ssl/OpenSslServerSessionContext.java | 4 +- .../handler/ssl/OpenSslSessionContext.java | 15 +- .../ReferenceCountedOpenSslClientContext.java | 121 +++++++------- .../ssl/ReferenceCountedOpenSslContext.java | 22 ++- .../ssl/ReferenceCountedOpenSslEngine.java | 2 +- .../ReferenceCountedOpenSslServerContext.java | 150 +++++++++--------- .../netty/handler/ssl/SslContextBuilder.java | 6 + ...OpenSslCachingKeyMaterialProviderTest.java | 77 +++++++++ .../ssl/OpenSslKeyMaterialProviderTest.java | 89 +++++++++++ pom.xml | 2 +- 16 files changed, 740 insertions(+), 264 deletions(-) create mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProvider.java create mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslCachingX509KeyManagerFactory.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslExtendedKeyMaterialManager.java create mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java create mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java create mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProviderTest.java create mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialProviderTest.java diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProvider.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProvider.java new file mode 100644 index 000000000000..db8779bdbd7a --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProvider.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.buffer.ByteBufAllocator; + +import javax.net.ssl.X509KeyManager; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * {@link OpenSslKeyMaterialProvider} that will cache the {@link OpenSslKeyMaterial} to reduce the overhead + * of parsing the chain and the key for generation of the material. + */ +final class OpenSslCachingKeyMaterialProvider extends OpenSslKeyMaterialProvider { + + private final ConcurrentMap cache = new ConcurrentHashMap(); + + OpenSslCachingKeyMaterialProvider(X509KeyManager keyManager, String password) { + super(keyManager, password); + } + + @Override + OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception { + OpenSslKeyMaterial material = cache.get(alias); + if (material == null) { + material = super.chooseKeyMaterial(allocator, alias); + if (material == null) { + // No keymaterial should be used. + return null; + } + + OpenSslKeyMaterial old = cache.putIfAbsent(alias, material); + if (old != null) { + material.release(); + material = old; + } + } + // We need to call retain() as we want to always have at least a refCnt() of 1 before destroy() was called. + return material.retain(); + } + + @Override + void destroy() { + // Remove and release all entries. + do { + Iterator iterator = cache.values().iterator(); + while (iterator.hasNext()) { + iterator.next().release(); + iterator.remove(); + } + } while (!cache.isEmpty()); + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingX509KeyManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingX509KeyManagerFactory.java new file mode 100644 index 000000000000..6581d9b73244 --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingX509KeyManagerFactory.java @@ -0,0 +1,60 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.KeyManagerFactorySpi; +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.X509KeyManager; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.X509Certificate; + +/** + * Wraps another {@link KeyManagerFactory} and caches its chains / certs for an alias for better performance when using + * {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT}. + * + * Because of the caching its important that the wrapped {@link KeyManagerFactory}s {@link X509KeyManager}s always + * return the same {@link X509Certificate} chain and {@link PrivateKey} for the same alias. + */ +public final class OpenSslCachingX509KeyManagerFactory extends KeyManagerFactory { + + public OpenSslCachingX509KeyManagerFactory(final KeyManagerFactory factory) { + super(new KeyManagerFactorySpi() { + @Override + protected void engineInit(KeyStore keyStore, char[] chars) + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { + factory.init(keyStore, chars); + } + + @Override + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) + throws InvalidAlgorithmParameterException { + factory.init(managerFactoryParameters); + } + + @Override + protected KeyManager[] engineGetKeyManagers() { + return factory.getKeyManagers(); + } + }, factory.getProvider(), factory.getAlgorithm()); + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslExtendedKeyMaterialManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslExtendedKeyMaterialManager.java deleted file mode 100644 index 38f6a7f723b9..000000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslExtendedKeyMaterialManager.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2016 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: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.X509ExtendedKeyManager; -import javax.security.auth.x500.X500Principal; - -final class OpenSslExtendedKeyMaterialManager extends OpenSslKeyMaterialManager { - - private final X509ExtendedKeyManager keyManager; - - OpenSslExtendedKeyMaterialManager(X509ExtendedKeyManager keyManager, String password) { - super(keyManager, password); - this.keyManager = keyManager; - } - - @Override - protected String chooseClientAlias(ReferenceCountedOpenSslEngine engine, String[] keyTypes, - X500Principal[] issuer) { - return keyManager.chooseEngineClientAlias(keyTypes, issuer, engine); - } - - @Override - protected String chooseServerAlias(ReferenceCountedOpenSslEngine engine, String type) { - return keyManager.chooseEngineServerAlias(type, null, engine); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java new file mode 100644 index 000000000000..749026807bd5 --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java @@ -0,0 +1,124 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.internal.tcnative.SSL; +import io.netty.util.AbstractReferenceCounted; +import io.netty.util.IllegalReferenceCountException; +import io.netty.util.ResourceLeakDetector; +import io.netty.util.ResourceLeakDetectorFactory; +import io.netty.util.ResourceLeakTracker; + +/** + * Holds references to the native key-material that is used by OpenSSL. + */ +final class OpenSslKeyMaterial extends AbstractReferenceCounted { + + private static final ResourceLeakDetector leakDetector = + ResourceLeakDetectorFactory.instance().newResourceLeakDetector(OpenSslKeyMaterial.class); + private final ResourceLeakTracker leak; + private long chain; + private long privateKey; + + OpenSslKeyMaterial(long chain, long privateKey) { + this.chain = chain; + this.privateKey = privateKey; + leak = leakDetector.track(this); + } + + /** + * Returns the pointer to the {@code STACK_OF(X509)} which holds the certificate chain. + */ + public long certificateChainAddress() { + if (refCnt() <= 0) { + throw new IllegalReferenceCountException(); + } + return chain; + } + + /** + * Returns the pointer to the {@code EVP_PKEY}. + */ + public long privateKeyAddress() { + if (refCnt() <= 0) { + throw new IllegalReferenceCountException(); + } + return privateKey; + } + + @Override + protected void deallocate() { + SSL.freeX509Chain(chain); + chain = 0; + SSL.freePrivateKey(privateKey); + privateKey = 0; + if (leak != null) { + boolean closed = leak.close(this); + assert closed; + } + } + + @Override + public OpenSslKeyMaterial retain() { + if (leak != null) { + leak.record(); + } + super.retain(); + return this; + } + + @Override + public OpenSslKeyMaterial retain(int increment) { + if (leak != null) { + leak.record(); + } + super.retain(increment); + return this; + } + + @Override + public OpenSslKeyMaterial touch() { + if (leak != null) { + leak.record(); + } + super.touch(); + return this; + } + + @Override + public OpenSslKeyMaterial touch(Object hint) { + if (leak != null) { + leak.record(hint); + } + return this; + } + + @Override + public boolean release() { + if (leak != null) { + leak.record(); + } + return super.release(); + } + + @Override + public boolean release(int decrement) { + if (leak != null) { + leak.record(); + } + return super.release(decrement); + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java index fd2a7ee8b804..f365dee279c5 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java @@ -15,11 +15,10 @@ */ package io.netty.handler.ssl; -import io.netty.buffer.ByteBufAllocator; -import io.netty.internal.tcnative.CertificateRequestedCallback; import io.netty.internal.tcnative.SSL; import javax.net.ssl.SSLException; +import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509KeyManager; import javax.security.auth.x500.X500Principal; import java.security.PrivateKey; @@ -29,14 +28,12 @@ import java.util.Map; import java.util.Set; -import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.freeBio; -import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.toBIO; /** * Manages key material for {@link OpenSslEngine}s and so set the right {@link PrivateKey}s and * {@link X509Certificate}s. */ -class OpenSslKeyMaterialManager { +final class OpenSslKeyMaterialManager { // Code in this class is inspired by code of conscrypts: // - https://android.googlesource.com/platform/external/ @@ -62,15 +59,13 @@ class OpenSslKeyMaterialManager { KEY_TYPES.put("DH_RSA", KEY_TYPE_DH_RSA); } - private final X509KeyManager keyManager; - private final String password; + private final OpenSslKeyMaterialProvider provider; - OpenSslKeyMaterialManager(X509KeyManager keyManager, String password) { - this.keyManager = keyManager; - this.password = password; + OpenSslKeyMaterialManager(OpenSslKeyMaterialProvider provider) { + this.provider = provider; } - void setKeyMaterial(ReferenceCountedOpenSslEngine engine) throws SSLException { + void setKeyMaterialServerSide(ReferenceCountedOpenSslEngine engine) throws SSLException { long ssl = engine.sslPointer(); String[] authMethods = SSL.authenticationMethods(ssl); Set aliases = new HashSet(authMethods.length); @@ -79,101 +74,62 @@ void setKeyMaterial(ReferenceCountedOpenSslEngine engine) throws SSLException { if (type != null) { String alias = chooseServerAlias(engine, type); if (alias != null && aliases.add(alias)) { - setKeyMaterial(ssl, alias, engine.alloc); + OpenSslKeyMaterial keyMaterial = null; + try { + keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias); + if (keyMaterial != null) { + SSL.setKeyMaterialServerSide( + ssl, keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress()); + } + } catch (SSLException e) { + throw e; + } catch (Exception e) { + throw new SSLException(e); + } finally { + if (keyMaterial != null) { + keyMaterial.release(); + } + } } } } } - CertificateRequestedCallback.KeyMaterial keyMaterial(ReferenceCountedOpenSslEngine engine, String[] keyTypes, - X500Principal[] issuer) throws SSLException { + void setKeyMaterialClientSide(ReferenceCountedOpenSslEngine engine, long certOut, long keyOut, String[] keyTypes, + X500Principal[] issuer) throws SSLException { String alias = chooseClientAlias(engine, keyTypes, issuer); - long keyBio = 0; - long keyCertChainBio = 0; - long pkey = 0; - long certChain = 0; - + OpenSslKeyMaterial keyMaterial = null; try { - // TODO: Should we cache these and so not need to do a memory copy all the time ? - X509Certificate[] certificates = keyManager.getCertificateChain(alias); - if (certificates == null || certificates.length == 0) { - return null; - } - - PrivateKey key = keyManager.getPrivateKey(alias); - keyCertChainBio = toBIO(engine.alloc, certificates); - certChain = SSL.parseX509Chain(keyCertChainBio); - if (key != null) { - keyBio = toBIO(engine.alloc, key); - pkey = SSL.parsePrivateKey(keyBio, password); + keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias); + if (keyMaterial != null) { + SSL.setKeyMaterialClientSide(engine.sslPointer(), certOut, keyOut, + keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress()); } - CertificateRequestedCallback.KeyMaterial material = new CertificateRequestedCallback.KeyMaterial( - certChain, pkey); - - // Reset to 0 so we do not free these. This is needed as the client certificate callback takes ownership - // of both the key and the certificate if they are returned from this method, and thus must not - // be freed here. - certChain = pkey = 0; - return material; } catch (SSLException e) { throw e; } catch (Exception e) { throw new SSLException(e); } finally { - freeBio(keyBio); - freeBio(keyCertChainBio); - SSL.freePrivateKey(pkey); - SSL.freeX509Chain(certChain); - } - } - - private void setKeyMaterial(long ssl, String alias, ByteBufAllocator allocator) throws SSLException { - long keyBio = 0; - long keyCertChainBio = 0; - long keyCertChainBio2 = 0; - - try { - // TODO: Should we cache these and so not need to do a memory copy all the time ? - X509Certificate[] certificates = keyManager.getCertificateChain(alias); - if (certificates == null || certificates.length == 0) { - return; - } - - PrivateKey key = keyManager.getPrivateKey(alias); - - // Only encode one time - PemEncoded encoded = PemX509Certificate.toPEM(allocator, true, certificates); - try { - keyCertChainBio = toBIO(allocator, encoded.retain()); - keyCertChainBio2 = toBIO(allocator, encoded.retain()); - - if (key != null) { - keyBio = toBIO(allocator, key); - } - SSL.setCertificateBio(ssl, keyCertChainBio, keyBio, password); - - // We may have more then one cert in the chain so add all of them now. - SSL.setCertificateChainBio(ssl, keyCertChainBio2, true); - } finally { - encoded.release(); + if (keyMaterial != null) { + keyMaterial.release(); } - } catch (SSLException e) { - throw e; - } catch (Exception e) { - throw new SSLException(e); - } finally { - freeBio(keyBio); - freeBio(keyCertChainBio); - freeBio(keyCertChainBio2); } } - protected String chooseClientAlias(@SuppressWarnings("unused") ReferenceCountedOpenSslEngine engine, + private String chooseClientAlias(ReferenceCountedOpenSslEngine engine, String[] keyTypes, X500Principal[] issuer) { - return keyManager.chooseClientAlias(keyTypes, issuer, null); + X509KeyManager manager = provider.keyManager(); + if (manager instanceof X509ExtendedKeyManager) { + return ((X509ExtendedKeyManager) manager).chooseEngineClientAlias(keyTypes, issuer, engine); + } + return manager.chooseClientAlias(keyTypes, issuer, null); } - protected String chooseServerAlias(@SuppressWarnings("unused") ReferenceCountedOpenSslEngine engine, String type) { - return keyManager.chooseServerAlias(type, null, null); + private String chooseServerAlias(ReferenceCountedOpenSslEngine engine, String type) { + X509KeyManager manager = provider.keyManager(); + if (manager instanceof X509ExtendedKeyManager) { + return ((X509ExtendedKeyManager) manager).chooseEngineServerAlias(type, null, engine); + } + return manager.chooseServerAlias(type, null, null); } } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java new file mode 100644 index 000000000000..9f4d6ee2fbf9 --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java @@ -0,0 +1,94 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.buffer.ByteBufAllocator; +import io.netty.internal.tcnative.SSL; + +import javax.net.ssl.X509KeyManager; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.toBIO; + +/** + * Provides {@link OpenSslKeyMaterial} for a given alias. + */ +class OpenSslKeyMaterialProvider { + + private final X509KeyManager keyManager; + private final String password; + + OpenSslKeyMaterialProvider(X509KeyManager keyManager, String password) { + this.keyManager = keyManager; + this.password = password; + } + + /** + * Returns the underlying {@link X509KeyManager} that is used. + */ + X509KeyManager keyManager() { + return keyManager; + } + + /** + * Returns the {@link OpenSslKeyMaterial} or {@code null} (if none) that should be used during the handshake by + * OpenSSL. + */ + OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception { + X509Certificate[] certificates = keyManager.getCertificateChain(alias); + if (certificates == null || certificates.length == 0) { + return null; + } + + PrivateKey key = keyManager.getPrivateKey(alias); + PemEncoded encoded = PemX509Certificate.toPEM(allocator, true, certificates); + long chainBio = 0; + long pkeyBio = 0; + long chain = 0; + long pkey = 0; + try { + chainBio = toBIO(allocator, encoded.retain()); + pkeyBio = toBIO(allocator, key); + chain = SSL.parseX509Chain(chainBio); + pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password); + OpenSslKeyMaterial keyMaterial = new OpenSslKeyMaterial(chain, pkey); + + // See the chain and pkey to 0 so we will not release it as the ownership was + // transferred to OpenSslKeyMaterial. + chain = 0; + pkey = 0; + return keyMaterial; + } finally { + SSL.freeBIO(chainBio); + SSL.freeBIO(pkeyBio); + if (chain != 0) { + SSL.freeX509Chain(chain); + } + if (pkey != 0) { + SSL.freePrivateKey(pkey); + } + encoded.release(); + } + } + + /** + * Will be invoked once the provider should be destroyed. + */ + void destroy() { + // NOOP. + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java index 8c92debfecde..691ee0b661bf 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java @@ -25,8 +25,8 @@ * {@link OpenSslSessionContext} implementation which offers extra methods which are only useful for the server-side. */ public final class OpenSslServerSessionContext extends OpenSslSessionContext { - OpenSslServerSessionContext(ReferenceCountedOpenSslContext context) { - super(context); + OpenSslServerSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) { + super(context, provider); } @Override diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java index 846a968735d8..9faefb138011 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java @@ -34,14 +34,21 @@ public abstract class OpenSslSessionContext implements SSLSessionContext { private static final Enumeration EMPTY = new EmptyEnumeration(); private final OpenSslSessionStats stats; + + // The OpenSslKeyMaterialProvider is not really used by the OpenSslSessionContext but only be stored here + // to make it easier to destroy it later because the ReferenceCountedOpenSslContext will hold a reference + // to OpenSslSessionContext. + private final OpenSslKeyMaterialProvider provider; + final ReferenceCountedOpenSslContext context; // IMPORTANT: We take the OpenSslContext and not just the long (which points the native instance) to prevent // the GC to collect OpenSslContext as this would also free the pointer and so could result in a // segfault when the user calls any of the methods here that try to pass the pointer down to the native // level. - OpenSslSessionContext(ReferenceCountedOpenSslContext context) { + OpenSslSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) { this.context = context; + this.provider = provider; stats = new OpenSslSessionStats(context); } @@ -123,6 +130,12 @@ public OpenSslSessionStats stats() { return stats; } + final void destroy() { + if (provider != null) { + provider.destroy(); + } + } + private static final class EmptyEnumeration implements Enumeration { @Override public boolean hasMoreElements() { diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java index b2135734e936..ea63bad6acde 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java @@ -31,9 +31,7 @@ import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; @@ -90,71 +88,82 @@ static OpenSslSessionContext newSessionContext(ReferenceCountedOpenSslContext th throw new IllegalArgumentException( "Either both keyCertChain and key needs to be null or none of them"); } + OpenSslKeyMaterialProvider keyMaterialProvider = null; try { - if (!OpenSsl.useKeyManagerFactory()) { - if (keyManagerFactory != null) { - throw new IllegalArgumentException( - "KeyManagerFactory not supported"); - } - if (keyCertChain != null/* && key != null*/) { - setKeyMaterial(ctx, keyCertChain, key, keyPassword); - } - } else { - // javadocs state that keyManagerFactory has precedent over keyCertChain - if (keyManagerFactory == null && keyCertChain != null) { - keyManagerFactory = buildKeyManagerFactory( - keyCertChain, key, keyPassword, keyManagerFactory); - } + try { + if (!OpenSsl.useKeyManagerFactory()) { + if (keyManagerFactory != null) { + throw new IllegalArgumentException( + "KeyManagerFactory not supported"); + } + if (keyCertChain != null/* && key != null*/) { + setKeyMaterial(ctx, keyCertChain, key, keyPassword); + } + } else { + // javadocs state that keyManagerFactory has precedent over keyCertChain + if (keyManagerFactory == null && keyCertChain != null) { + keyMaterialProvider = new OpenSslCachingKeyMaterialProvider( + chooseX509KeyManager(buildKeyManagerFactory(keyCertChain, key, keyPassword, null) + .getKeyManagers()), keyPassword); + } else if (keyManagerFactory != null) { + keyMaterialProvider = providerFor(keyManagerFactory, keyPassword); + } - if (keyManagerFactory != null) { - X509KeyManager keyManager = chooseX509KeyManager(keyManagerFactory.getKeyManagers()); - OpenSslKeyMaterialManager materialManager = useExtendedKeyManager(keyManager) ? - new OpenSslExtendedKeyMaterialManager( - (X509ExtendedKeyManager) keyManager, keyPassword) : - new OpenSslKeyMaterialManager(keyManager, keyPassword); - SSLContext.setCertRequestedCallback(ctx, new OpenSslCertificateRequestedCallback( - engineMap, materialManager)); + if (keyMaterialProvider != null) { + OpenSslKeyMaterialManager materialManager = new OpenSslKeyMaterialManager(keyMaterialProvider); + SSLContext.setCertRequestedCallback(ctx, new OpenSslCertificateRequestedCallback( + engineMap, materialManager)); + } } + } catch (Exception e) { + throw new SSLException("failed to set certificate and key", e); } - } catch (Exception e) { - throw new SSLException("failed to set certificate and key", e); - } - SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH); + SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH); - try { - if (trustCertCollection != null) { - trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory); - } else if (trustManagerFactory == null) { - trustManagerFactory = TrustManagerFactory.getInstance( - TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init((KeyStore) null); - } - final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers()); + try { + if (trustCertCollection != null) { + trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory); + } else if (trustManagerFactory == null) { + trustManagerFactory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore) null); + } + final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers()); - // IMPORTANT: The callbacks set for verification must be static to prevent memory leak as - // otherwise the context can never be collected. This is because the JNI code holds - // a global reference to the callbacks. - // - // See https://github.com/netty/netty/issues/5372 + // IMPORTANT: The callbacks set for verification must be static to prevent memory leak as + // otherwise the context can never be collected. This is because the JNI code holds + // a global reference to the callbacks. + // + // See https://github.com/netty/netty/issues/5372 - // Use this to prevent an error when running on java < 7 - if (useExtendedTrustManager(manager)) { - SSLContext.setCertVerifyCallback(ctx, - new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager)); - } else { - SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager)); + // Use this to prevent an error when running on java < 7 + if (useExtendedTrustManager(manager)) { + SSLContext.setCertVerifyCallback(ctx, + new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager)); + } else { + SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager)); + } + } catch (Exception e) { + if (keyMaterialProvider != null) { + keyMaterialProvider.destroy(); + } + throw new SSLException("unable to setup trustmanager", e); + } + OpenSslClientSessionContext context = new OpenSslClientSessionContext(thiz, keyMaterialProvider); + keyMaterialProvider = null; + return context; + } finally { + if (keyMaterialProvider != null) { + keyMaterialProvider.destroy(); } - } catch (Exception e) { - throw new SSLException("unable to setup trustmanager", e); } - return new OpenSslClientSessionContext(thiz); } // No cache is currently supported for client side mode. static final class OpenSslClientSessionContext extends OpenSslSessionContext { - OpenSslClientSessionContext(ReferenceCountedOpenSslContext context) { - super(context); + OpenSslClientSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) { + super(context, provider); } @Override @@ -232,7 +241,8 @@ private static final class OpenSslCertificateRequestedCallback implements Certif } @Override - public KeyMaterial requested(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) { + public void requested( + long ssl, long certOut, long keyOut, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) { final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); try { final Set keyTypesSet = supportedClientKeyTypes(keyTypeBytes); @@ -246,13 +256,12 @@ public KeyMaterial requested(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEnco issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]); } } - return keyManagerHolder.keyMaterial(engine, keyTypes, issuers); + keyManagerHolder.setKeyMaterialClientSide(engine, certOut, keyOut, keyTypes, issuers); } catch (Throwable cause) { logger.debug("request of key failed", cause); SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem"); e.initCause(cause); engine.handshakeException = e; - return null; } } diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index 84cf6da0a787..9a1a1c02454c 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -49,11 +49,11 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.TrustManager; -import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; @@ -486,6 +486,7 @@ private void destroy() { SSLContext.free(ctx); ctx = 0; + sessionContext().destroy(); } } finally { writerLock.unlock(); @@ -566,10 +567,6 @@ static boolean useExtendedTrustManager(X509TrustManager trustManager) { return PlatformDependent.javaVersion() >= 7 && trustManager instanceof X509ExtendedTrustManager; } - static boolean useExtendedKeyManager(X509KeyManager keyManager) { - return PlatformDependent.javaVersion() >= 7 && keyManager instanceof X509ExtendedKeyManager; - } - @Override public final int refCnt() { return refCnt.refCnt(); @@ -819,4 +816,19 @@ private static long newBIO(ByteBuf buffer) throws Exception { buffer.release(); } } + + /** + * Returns the {@link OpenSslKeyMaterialProvider} that should be used for OpenSSL. Depending on the given + * {@link KeyManagerFactory} this may cache the {@link OpenSslKeyMaterial} for better performance if it can + * ensure that the same material is always returned for the same alias. + */ + static OpenSslKeyMaterialProvider providerFor(KeyManagerFactory factory, String password) { + X509KeyManager keyManager = chooseX509KeyManager(factory.getKeyManagers()); + if (factory instanceof OpenSslCachingX509KeyManagerFactory) { + // The user explicit used OpenSslCachingX509KeyManagerFactory which signals us that its fine to cache. + return new OpenSslCachingKeyMaterialProvider(keyManager, password); + } + // We can not be sure if the material may change at runtime so we will not cache it. + return new OpenSslKeyMaterialProvider(keyManager, password); + } } diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 788a620f918f..658bb015585e 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -1567,7 +1567,7 @@ private SSLEngineResult.HandshakeStatus handshake() throws SSLException { if (!certificateSet && keyMaterialManager != null) { certificateSet = true; - keyMaterialManager.setKeyMaterial(this); + keyMaterialManager.setKeyMaterialServerSide(this); } int code = SSL.doHandshake(ssl); diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java index 8eb4940c401c..b8f0254ef5f8 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java @@ -29,9 +29,7 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLException; import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import static io.netty.util.internal.ObjectUtil.checkNotNull; @@ -107,87 +105,97 @@ static ServerContext newSessionContext(ReferenceCountedOpenSslContext thiz, long String keyPassword, KeyManagerFactory keyManagerFactory) throws SSLException { ServerContext result = new ServerContext(); + OpenSslKeyMaterialProvider keyMaterialProvider = null; try { - SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH); - if (!OpenSsl.useKeyManagerFactory()) { - if (keyManagerFactory != null) { - throw new IllegalArgumentException( - "KeyManagerFactory not supported"); - } - checkNotNull(keyCertChain, "keyCertChain"); - - setKeyMaterial(ctx, keyCertChain, key, keyPassword); - } else { - // javadocs state that keyManagerFactory has precedent over keyCertChain, and we must have a - // keyManagerFactory for the server so build one if it is not specified. - if (keyManagerFactory == null) { - keyManagerFactory = buildKeyManagerFactory( - keyCertChain, key, keyPassword, keyManagerFactory); + try { + SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH); + if (!OpenSsl.useKeyManagerFactory()) { + if (keyManagerFactory != null) { + throw new IllegalArgumentException( + "KeyManagerFactory not supported"); + } + checkNotNull(keyCertChain, "keyCertChain"); + + setKeyMaterial(ctx, keyCertChain, key, keyPassword); + } else { + // javadocs state that keyManagerFactory has precedent over keyCertChain, and we must have a + // keyManagerFactory for the server so build one if it is not specified. + if (keyManagerFactory == null) { + keyMaterialProvider = new OpenSslCachingKeyMaterialProvider( + chooseX509KeyManager(buildKeyManagerFactory(keyCertChain, key, keyPassword, null) + .getKeyManagers()), keyPassword); + } else { + keyMaterialProvider = providerFor(keyManagerFactory, keyPassword); + } + + result.keyMaterialManager = new OpenSslKeyMaterialManager(keyMaterialProvider); } - X509KeyManager keyManager = chooseX509KeyManager(keyManagerFactory.getKeyManagers()); - result.keyMaterialManager = useExtendedKeyManager(keyManager) ? - new OpenSslExtendedKeyMaterialManager( - (X509ExtendedKeyManager) keyManager, keyPassword) : - new OpenSslKeyMaterialManager(keyManager, keyPassword); - } - } catch (Exception e) { - throw new SSLException("failed to set certificate and key", e); - } - try { - if (trustCertCollection != null) { - trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory); - } else if (trustManagerFactory == null) { - // Mimic the way SSLContext.getInstance(KeyManager[], null, null) works - trustManagerFactory = TrustManagerFactory.getInstance( - TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init((KeyStore) null); + } catch (Exception e) { + throw new SSLException("failed to set certificate and key", e); } + try { + if (trustCertCollection != null) { + trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory); + } else if (trustManagerFactory == null) { + // Mimic the way SSLContext.getInstance(KeyManager[], null, null) works + trustManagerFactory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore) null); + } - final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers()); - - // IMPORTANT: The callbacks set for verification must be static to prevent memory leak as - // otherwise the context can never be collected. This is because the JNI code holds - // a global reference to the callbacks. - // - // See https://github.com/netty/netty/issues/5372 + final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers()); - // Use this to prevent an error when running on java < 7 - if (useExtendedTrustManager(manager)) { - SSLContext.setCertVerifyCallback(ctx, - new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager)); - } else { - SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager)); - } + // IMPORTANT: The callbacks set for verification must be static to prevent memory leak as + // otherwise the context can never be collected. This is because the JNI code holds + // a global reference to the callbacks. + // + // See https://github.com/netty/netty/issues/5372 + + // Use this to prevent an error when running on java < 7 + if (useExtendedTrustManager(manager)) { + SSLContext.setCertVerifyCallback(ctx, + new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager)); + } else { + SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager)); + } - X509Certificate[] issuers = manager.getAcceptedIssuers(); - if (issuers != null && issuers.length > 0) { - long bio = 0; - try { - bio = toBIO(ByteBufAllocator.DEFAULT, issuers); - if (!SSLContext.setCACertificateBio(ctx, bio)) { - throw new SSLException("unable to setup accepted issuers for trustmanager " + manager); + X509Certificate[] issuers = manager.getAcceptedIssuers(); + if (issuers != null && issuers.length > 0) { + long bio = 0; + try { + bio = toBIO(ByteBufAllocator.DEFAULT, issuers); + if (!SSLContext.setCACertificateBio(ctx, bio)) { + throw new SSLException("unable to setup accepted issuers for trustmanager " + manager); + } + } finally { + freeBio(bio); } - } finally { - freeBio(bio); } + + if (PlatformDependent.javaVersion() >= 8) { + // Only do on Java8+ as SNIMatcher is not supported in earlier releases. + // IMPORTANT: The callbacks set for hostname matching must be static to prevent memory leak as + // otherwise the context can never be collected. This is because the JNI code holds + // a global reference to the matcher. + SSLContext.setSniHostnameMatcher(ctx, new OpenSslSniHostnameMatcher(engineMap)); + } + } catch (SSLException e) { + throw e; + } catch (Exception e) { + throw new SSLException("unable to setup trustmanager", e); } - if (PlatformDependent.javaVersion() >= 8) { - // Only do on Java8+ as SNIMatcher is not supported in earlier releases. - // IMPORTANT: The callbacks set for hostname matching must be static to prevent memory leak as - // otherwise the context can never be collected. This is because the JNI code holds - // a global reference to the matcher. - SSLContext.setSniHostnameMatcher(ctx, new OpenSslSniHostnameMatcher(engineMap)); + result.sessionContext = new OpenSslServerSessionContext(thiz, keyMaterialProvider); + result.sessionContext.setSessionIdContext(ID); + + keyMaterialProvider = null; + + return result; + } finally { + if (keyMaterialProvider != null) { + keyMaterialProvider.destroy(); } - } catch (SSLException e) { - throw e; - } catch (Exception e) { - throw new SSLException("unable to setup trustmanager", e); } - - result.sessionContext = new OpenSslServerSessionContext(thiz); - result.sessionContext.setSessionIdContext(ID); - return result; } private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier { diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java index 2e3c55b9cb2d..48617505af59 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java @@ -121,6 +121,9 @@ public static SslContextBuilder forServer( /** * Creates a builder for new server-side {@link SslContext}. * + * If you use {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} consider using + * {@link OpenSslCachingX509KeyManagerFactory}. + * * @param keyManagerFactory non-{@code null} factory for server's private key * @see #keyManager(KeyManagerFactory) */ @@ -335,6 +338,9 @@ public SslContextBuilder keyManager(PrivateKey key, String keyPassword, X509Cert * if the used openssl version is 1.0.1+. You can check if your openssl version supports using a * {@link KeyManagerFactory} by calling {@link OpenSsl#supportsKeyManagerFactory()}. If this is not the case * you must use {@link #keyManager(File, File)} or {@link #keyManager(File, File, String)}. + * + * If you use {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} consider using + * {@link OpenSslCachingX509KeyManagerFactory}. */ public SslContextBuilder keyManager(KeyManagerFactory keyManagerFactory) { if (forServer) { diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProviderTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProviderTest.java new file mode 100644 index 000000000000..e284da15179a --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProviderTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.buffer.UnpooledByteBufAllocator; +import org.junit.Assert; +import org.junit.Test; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.X509KeyManager; + +import static org.junit.Assert.*; + +public class OpenSslCachingKeyMaterialProviderTest extends OpenSslKeyMaterialProviderTest { + + @Override + protected KeyManagerFactory newKeyManagerFactory() throws Exception { + return new OpenSslCachingX509KeyManagerFactory(super.newKeyManagerFactory()); + } + + @Override + protected OpenSslKeyMaterialProvider newMaterialProvider(X509KeyManager manager, String password) { + return new OpenSslCachingKeyMaterialProvider(manager, password); + } + + @Override + protected void assertRelease(OpenSslKeyMaterial material) { + Assert.assertFalse(material.release()); + } + + @Override + protected Class providerClass() { + return OpenSslCachingKeyMaterialProvider.class; + } + + @Test + public void testMaterialCached() throws Exception { + X509KeyManager manager = ReferenceCountedOpenSslContext.chooseX509KeyManager( + newKeyManagerFactory().getKeyManagers()); + OpenSslKeyMaterialProvider provider = newMaterialProvider(manager, PASSWORD); + + OpenSslKeyMaterial material = provider.chooseKeyMaterial(UnpooledByteBufAllocator.DEFAULT, EXISTING_ALIAS); + assertNotNull(material); + assertNotEquals(0, material.certificateChainAddress()); + assertNotEquals(0, material.privateKeyAddress()); + assertEquals(2, material.refCnt()); + + OpenSslKeyMaterial material2 = provider.chooseKeyMaterial(UnpooledByteBufAllocator.DEFAULT, EXISTING_ALIAS); + assertNotNull(material2); + assertEquals(material.certificateChainAddress(), material2.certificateChainAddress()); + assertEquals(material.privateKeyAddress(), material2.privateKeyAddress()); + assertEquals(3, material.refCnt()); + assertEquals(3, material2.refCnt()); + + assertFalse(material.release()); + assertFalse(material2.release()); + + // After this the material should have been released. + provider.destroy(); + + assertEquals(0, material.refCnt()); + assertEquals(0, material2.refCnt()); + } +} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialProviderTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialProviderTest.java new file mode 100644 index 000000000000..651db93f089b --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialProviderTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.buffer.UnpooledByteBufAllocator; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.X509KeyManager; + +import java.security.KeyStore; + +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; + +public class OpenSslKeyMaterialProviderTest { + + static final String PASSWORD = "example"; + static final String EXISTING_ALIAS = "1"; + private static final String NON_EXISTING_ALIAS = "nonexisting"; + + @BeforeClass + public static void checkOpenSsl() { + assumeTrue(OpenSsl.isAvailable()); + } + + protected KeyManagerFactory newKeyManagerFactory() throws Exception { + char[] password = PASSWORD.toCharArray(); + final KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load(getClass().getResourceAsStream("mutual_auth_server.p12"), password); + + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keystore, password); + return kmf; + } + + protected OpenSslKeyMaterialProvider newMaterialProvider(X509KeyManager manager, String password) { + return new OpenSslKeyMaterialProvider(manager, password); + } + + protected Class providerClass() { + return OpenSslKeyMaterialProvider.class; + } + + protected void assertRelease(OpenSslKeyMaterial material) { + assertTrue(material.release()); + } + + @Test + public void testChooseKeyMaterial() throws Exception { + X509KeyManager manager = ReferenceCountedOpenSslContext.chooseX509KeyManager( + newKeyManagerFactory().getKeyManagers()); + OpenSslKeyMaterialProvider provider = newMaterialProvider(manager, PASSWORD); + OpenSslKeyMaterial nonExistingMaterial = provider.chooseKeyMaterial( + UnpooledByteBufAllocator.DEFAULT, NON_EXISTING_ALIAS); + assertNull(nonExistingMaterial); + + OpenSslKeyMaterial material = provider.chooseKeyMaterial(UnpooledByteBufAllocator.DEFAULT, EXISTING_ALIAS); + assertNotNull(material); + assertNotEquals(0, material.certificateChainAddress()); + assertNotEquals(0, material.privateKeyAddress()); + assertRelease(material); + + provider.destroy(); + } + + @Test + public void testChooseTheCorrectProvider() throws Exception { + OpenSslKeyMaterialProvider provider = ReferenceCountedOpenSslContext.providerFor( + newKeyManagerFactory(), PASSWORD); + assertEquals(providerClass(), provider.getClass()); + provider.destroy(); + } +} diff --git a/pom.xml b/pom.xml index de59d8e71262..a3a1622dcd6e 100644 --- a/pom.xml +++ b/pom.xml @@ -221,7 +221,7 @@ fedora netty-tcnative - 2.0.10.Final + 2.0.11.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber From 9ffdec302e21e6a027275b2a1ac9a17a223a024f Mon Sep 17 00:00:00 2001 From: Alexey Kachayev Date: Fri, 22 Jun 2018 19:50:22 +0300 Subject: [PATCH 059/417] Make results of handler proxy tests reproducible Motivation: `ProxyHandlerTest` relies on random values to run tests: first to shuffle collection of test items and lately to set configuration flag for `AUTO_READ`. While the purpose of randomization is clear, it's still impossible to reproduce the same sequence of test cases when something went wrong. For `AUTO_READ` it's even impossible to tell what flag was set when the particular test failed. Modifications: * Test runner now log seed values that was used for shuffling, so you can take one and put in your tests to "freeze" them while debugging (pretty common approach with randomized tests) * `SuccessItemTest` is split into 2 different use cases: for AUTO_READ flag set to "on" and "off" Result: You can reproduce specific tests results now. --- .../netty/handler/proxy/ProxyHandlerTest.java | 138 +++++++++++++++--- 1 file changed, 119 insertions(+), 19 deletions(-) diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java b/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java index e79bed7cf50f..047b2af09b32 100644 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java +++ b/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java @@ -63,8 +63,8 @@ import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import java.util.Random; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @@ -130,15 +130,27 @@ public class ProxyHandlerTest { deadSocks5Proxy, interSocks5Proxy, anonSocks5Proxy, socks5Proxy ); + // set to non-zero value in case you need predictable shuffling of test cases + // look for "Seed used: *" debug message in test logs + private static final long reproducibleSeed = 0L; + @Parameters(name = "{index}: {0}") public static List testItems() { + List items = Arrays.asList( // HTTP ------------------------------------------------------- new SuccessTestItem( - "Anonymous HTTP proxy: successful connection", + "Anonymous HTTP proxy: successful connection, AUTO_READ on", + DESTINATION, + true, + new HttpProxyHandler(anonHttpProxy.address())), + + new SuccessTestItem( + "Anonymous HTTP proxy: successful connection, AUTO_READ off", DESTINATION, + false, new HttpProxyHandler(anonHttpProxy.address())), new FailureTestItem( @@ -152,8 +164,15 @@ public static List testItems() { new HttpProxyHandler(httpProxy.address())), new SuccessTestItem( - "HTTP proxy: successful connection", + "HTTP proxy: successful connection, AUTO_READ on", + DESTINATION, + true, + new HttpProxyHandler(httpProxy.address(), USERNAME, PASSWORD)), + + new SuccessTestItem( + "HTTP proxy: successful connection, AUTO_READ off", DESTINATION, + false, new HttpProxyHandler(httpProxy.address(), USERNAME, PASSWORD)), new FailureTestItem( @@ -173,8 +192,16 @@ public static List testItems() { // HTTPS ------------------------------------------------------ new SuccessTestItem( - "Anonymous HTTPS proxy: successful connection", + "Anonymous HTTPS proxy: successful connection, AUTO_READ on", + DESTINATION, + true, + clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), + new HttpProxyHandler(anonHttpsProxy.address())), + + new SuccessTestItem( + "Anonymous HTTPS proxy: successful connection, AUTO_READ off", DESTINATION, + false, clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), new HttpProxyHandler(anonHttpsProxy.address())), @@ -191,8 +218,16 @@ public static List testItems() { new HttpProxyHandler(httpsProxy.address())), new SuccessTestItem( - "HTTPS proxy: successful connection", + "HTTPS proxy: successful connection, AUTO_READ on", DESTINATION, + true, + clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), + new HttpProxyHandler(httpsProxy.address(), USERNAME, PASSWORD)), + + new SuccessTestItem( + "HTTPS proxy: successful connection, AUTO_READ off", + DESTINATION, + false, clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), new HttpProxyHandler(httpsProxy.address(), USERNAME, PASSWORD)), @@ -216,8 +251,15 @@ public static List testItems() { // SOCKS4 ----------------------------------------------------- new SuccessTestItem( - "Anonymous SOCKS4: successful connection", + "Anonymous SOCKS4: successful connection, AUTO_READ on", + DESTINATION, + true, + new Socks4ProxyHandler(anonSocks4Proxy.address())), + + new SuccessTestItem( + "Anonymous SOCKS4: successful connection, AUTO_READ off", DESTINATION, + false, new Socks4ProxyHandler(anonSocks4Proxy.address())), new FailureTestItem( @@ -231,8 +273,15 @@ public static List testItems() { new Socks4ProxyHandler(socks4Proxy.address())), new SuccessTestItem( - "SOCKS4: successful connection", + "SOCKS4: successful connection, AUTO_READ on", DESTINATION, + true, + new Socks4ProxyHandler(socks4Proxy.address(), USERNAME)), + + new SuccessTestItem( + "SOCKS4: successful connection, AUTO_READ off", + DESTINATION, + false, new Socks4ProxyHandler(socks4Proxy.address(), USERNAME)), new FailureTestItem( @@ -252,8 +301,15 @@ public static List testItems() { // SOCKS5 ----------------------------------------------------- new SuccessTestItem( - "Anonymous SOCKS5: successful connection", + "Anonymous SOCKS5: successful connection, AUTO_READ on", + DESTINATION, + true, + new Socks5ProxyHandler(anonSocks5Proxy.address())), + + new SuccessTestItem( + "Anonymous SOCKS5: successful connection, AUTO_READ off", DESTINATION, + false, new Socks5ProxyHandler(anonSocks5Proxy.address())), new FailureTestItem( @@ -267,8 +323,15 @@ public static List testItems() { new Socks5ProxyHandler(socks5Proxy.address())), new SuccessTestItem( - "SOCKS5: successful connection", + "SOCKS5: successful connection, AUTO_READ on", + DESTINATION, + true, + new Socks5ProxyHandler(socks5Proxy.address(), USERNAME, PASSWORD)), + + new SuccessTestItem( + "SOCKS5: successful connection, AUTO_READ off", DESTINATION, + false, new Socks5ProxyHandler(socks5Proxy.address(), USERNAME, PASSWORD)), new FailureTestItem( @@ -288,8 +351,20 @@ public static List testItems() { // HTTP + HTTPS + SOCKS4 + SOCKS5 new SuccessTestItem( - "Single-chain: successful connection", + "Single-chain: successful connection, AUTO_READ on", + DESTINATION, + true, + new Socks5ProxyHandler(interSocks5Proxy.address()), // SOCKS5 + new Socks4ProxyHandler(interSocks4Proxy.address()), // SOCKS4 + clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), + new HttpProxyHandler(interHttpsProxy.address()), // HTTPS + new HttpProxyHandler(interHttpProxy.address()), // HTTP + new HttpProxyHandler(anonHttpProxy.address())), + + new SuccessTestItem( + "Single-chain: successful connection, AUTO_READ off", DESTINATION, + false, new Socks5ProxyHandler(interSocks5Proxy.address()), // SOCKS5 new Socks4ProxyHandler(interSocks4Proxy.address()), // SOCKS4 clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), @@ -300,8 +375,9 @@ public static List testItems() { // (HTTP + HTTPS + SOCKS4 + SOCKS5) * 2 new SuccessTestItem( - "Double-chain: successful connection", + "Double-chain: successful connection, AUTO_READ on", DESTINATION, + true, new Socks5ProxyHandler(interSocks5Proxy.address()), // SOCKS5 new Socks4ProxyHandler(interSocks4Proxy.address()), // SOCKS4 clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), @@ -312,8 +388,23 @@ public static List testItems() { clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), new HttpProxyHandler(interHttpsProxy.address()), // HTTPS new HttpProxyHandler(interHttpProxy.address()), // HTTP - new HttpProxyHandler(anonHttpProxy.address())) + new HttpProxyHandler(anonHttpProxy.address())), + new SuccessTestItem( + "Double-chain: successful connection, AUTO_READ off", + DESTINATION, + false, + new Socks5ProxyHandler(interSocks5Proxy.address()), // SOCKS5 + new Socks4ProxyHandler(interSocks4Proxy.address()), // SOCKS4 + clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), + new HttpProxyHandler(interHttpsProxy.address()), // HTTPS + new HttpProxyHandler(interHttpProxy.address()), // HTTP + new Socks5ProxyHandler(interSocks5Proxy.address()), // SOCKS5 + new Socks4ProxyHandler(interSocks4Proxy.address()), // SOCKS4 + clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), + new HttpProxyHandler(interHttpsProxy.address()), // HTTPS + new HttpProxyHandler(interHttpProxy.address()), // HTTP + new HttpProxyHandler(anonHttpProxy.address())) ); // Convert the test items to the list of constructor parameters. @@ -323,7 +414,9 @@ public static List testItems() { } // Randomize the execution order to increase the possibility of exposing failure dependencies. - Collections.shuffle(params); + long seed = (reproducibleSeed == 0L) ? System.currentTimeMillis() : reproducibleSeed; + logger.debug("Seed used: {}\n", seed); + Collections.shuffle(params, new Random(seed)); return params; } @@ -337,9 +430,7 @@ public static void stopServers() { private final TestItem testItem; - public ProxyHandlerTest(TestItem testItem) { - this.testItem = testItem; - } + public ProxyHandlerTest(TestItem testItem) { this.testItem = testItem; } @Before public void clearServerExceptions() throws Exception { @@ -516,8 +607,16 @@ public String toString() { private static final class SuccessTestItem extends TestItem { private final int expectedEventCount; - - SuccessTestItem(String name, InetSocketAddress destination, ChannelHandler... clientHandlers) { + // Probably we need to be more flexible here and as for the configuration map, + // not a single key. But as far as it works for now, I'm leaving the impl. + // as is, in case we need to cover more cases (like, AUTO_CLOSE, TCP_NODELAY etc) + // feel free to replace this boolean with either config or method to setup bootstrap + private final boolean autoRead; + + SuccessTestItem(String name, + InetSocketAddress destination, + boolean autoRead, + ChannelHandler... clientHandlers) { super(name, destination, clientHandlers); int expectedEventCount = 0; for (ChannelHandler h: clientHandlers) { @@ -527,6 +626,7 @@ private static final class SuccessTestItem extends TestItem { } this.expectedEventCount = expectedEventCount; + this.autoRead = autoRead; } @Override @@ -535,7 +635,7 @@ protected void test() throws Exception { Bootstrap b = new Bootstrap(); b.group(group); b.channel(NioSocketChannel.class); - b.option(ChannelOption.AUTO_READ, ThreadLocalRandom.current().nextBoolean()); + b.option(ChannelOption.AUTO_READ, this.autoRead); b.resolver(NoopAddressResolverGroup.INSTANCE); b.handler(new ChannelInitializer() { @Override From fa4e28ba1ca4e81aa0ee4c756b719802e77d9817 Mon Sep 17 00:00:00 2001 From: Alexey Kachayev Date: Fri, 22 Jun 2018 22:54:34 +0300 Subject: [PATCH 060/417] Fix random number generators in WebSocketUtil Motivation: Implementation of WebSocketUtil/randomNumber is incorrect and might violate the API returning values > maximum specified. Modifications: * WebSocketUtil/randomNumber is reimplemented, the idea of the solution described in the comment in the code * Implementation of WebSocketUtil/randomBytes changed to nextBytes method * PlatformDependet.threadLocalRandom is used instead of Math.random to improve efficiency * Added test cases to check random numbers generator * To ensure corretness, we now assert that min < max when generating random number Result: WebSocketUtil/randomNumber always produces correct result. Covers https://github.com/netty/netty/issues/8023 --- .../codec/http/websocketx/WebSocketUtil.java | 31 ++++++++++--- .../WebSocketClientHandshakerTest.java | 4 +- .../http/websocketx/WebSocketUtilTest.java | 43 +++++++++++++++++++ 3 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketUtilTest.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketUtil.java index defdd41f3c08..b6a138b098cd 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketUtil.java @@ -20,6 +20,7 @@ import io.netty.handler.codec.base64.Base64; import io.netty.util.CharsetUtil; import io.netty.util.concurrent.FastThreadLocal; +import io.netty.util.internal.PlatformDependent; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -105,11 +106,7 @@ static String base64(byte[] data) { */ static byte[] randomBytes(int size) { byte[] bytes = new byte[size]; - - for (int index = 0; index < size; index++) { - bytes[index] = (byte) randomNumber(0, 255); - } - + PlatformDependent.threadLocalRandom().nextBytes(bytes); return bytes; } @@ -121,7 +118,29 @@ static byte[] randomBytes(int size) { * @return A pseudo-random number */ static int randomNumber(int minimum, int maximum) { - return (int) (Math.random() * maximum + minimum); + assert minimum < maximum; + double fraction = PlatformDependent.threadLocalRandom().nextDouble(); + + // the idea here is that nextDouble gives us a random value + // + // 0 <= fraction <= 1 + // + // the distance from min to max declared as + // + // dist = max - min + // + // satisfies the following + // + // min + dist = max + // + // taking into account + // + // 0 <= fraction * dist <= dist + // + // we've got + // + // min <= min + fraction * dist <= max + return (int) (minimum + fraction * (maximum - minimum)); } /** diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerTest.java index de1594b85823..2054af513f90 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerTest.java @@ -240,8 +240,8 @@ protected WebSocketFrameEncoder newWebSocketEncoder() { } }; - byte[] data = new byte[24]; - PlatformDependent.threadLocalRandom().nextBytes(data); + // use randomBytes helper from utils to check that it functions properly + byte[] data = WebSocketUtil.randomBytes(24); // Create a EmbeddedChannel which we will use to encode a BinaryWebsocketFrame to bytes and so use these // to test the actual handshaker. diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketUtilTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketUtilTest.java new file mode 100644 index 000000000000..e40501951ab6 --- /dev/null +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketUtilTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.http.websocketx; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class WebSocketUtilTest { + + // how many times do we want to run each random variable checker + private static final int NUM_ITERATIONS = 1000; + + private static void assertRandomWithinBoundaries(int min, int max) { + int r = WebSocketUtil.randomNumber(min, max); + assertTrue(min <= r && r <= max); + } + + @Test + public void testRandomNumberGenerator() { + int iteration = 0; + while (++iteration < NUM_ITERATIONS) { + assertRandomWithinBoundaries(0, 1); + assertRandomWithinBoundaries(0, 1); + assertRandomWithinBoundaries(-1, 1); + assertRandomWithinBoundaries(-1, 0); + } + } + +} From 028d9dcf0f5ae83ccf1c2377c6465e2ad196766b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 25 Jun 2018 13:13:51 +0200 Subject: [PATCH 061/417] Update to java 11+ea19 Motivation: A new java 11 EA version was released. Modifications: Update to java 11+ea19 Result: Use latest java 11 release. --- docker/docker-compose.centos-6.111.yaml | 2 +- docker/docker-compose.centos-7.111.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml index e11c2d87f167..61e21b636b19 100644 --- a/docker/docker-compose.centos-6.111.yaml +++ b/docker/docker-compose.centos-6.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "1.11.0-18" + java_version : "1.11.0-19" test: image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-7.111.yaml b/docker/docker-compose.centos-7.111.yaml index 4a1d1eea2dfb..aea7143e8622 100644 --- a/docker/docker-compose.centos-7.111.yaml +++ b/docker/docker-compose.centos-7.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "1.11.0-18" + java_version : "1.11.0-19" test: image: netty:centos-7-1.11 From 00afb19d7a37de21b35ce4f6cb3fa7f74809f2ab Mon Sep 17 00:00:00 2001 From: Alexey Kachayev Date: Mon, 25 Jun 2018 18:10:16 +0300 Subject: [PATCH 062/417] Get rid of deprecated SslContext methods in handler-proxy tests Motivation: ProxyHandlerTest package uses deprecated methods SslContext.newServerContext and SslContext.newClientContext. Modifications: SslContextBuilder is used to build server and client SslContext. Result: Less deprecated method in the code. --- .../test/java/io/netty/handler/proxy/ProxyHandlerTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java b/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java index 047b2af09b32..6f0f325d641a 100644 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java +++ b/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java @@ -35,6 +35,7 @@ import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.resolver.NoopAddressResolverGroup; @@ -91,8 +92,8 @@ public class ProxyHandlerTest { SslContext cctx; try { SelfSignedCertificate ssc = new SelfSignedCertificate(); - sctx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey()); - cctx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE); + sctx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); + cctx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build(); } catch (Exception e) { throw new Error(e); } From 5e42e758bebe489574ee1dc6ed8b1ed3db54792b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 25 Jun 2018 21:14:37 +0200 Subject: [PATCH 063/417] Also remove the id from the DnsQueryContextManager if query fails due parent Channel activation error. Motivation: Whenever we fail the query we should also remove the id from the DnsQueryContextManager. Modifications: Remove the id from the DnsQueryContextManager if we fail the query because the channel failed to become active. Result: More correct code. --- .../src/main/java/io/netty/resolver/dns/DnsQueryContext.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java index 12196f184ca9..3aee7686d31c 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java @@ -115,10 +115,13 @@ private void sendQuery(final DnsQuery query, final ChannelPromise writePromise) } else { parent.channelFuture.addListener(new GenericFutureListener>() { @Override - public void operationComplete(Future future) throws Exception { + public void operationComplete(Future future) { if (future.isSuccess()) { writeQuery(query, writePromise); } else { + // Remove the id from the manager as we fail the query. + parent.queryContextManager.remove(nameServerAddr(), id); + Throwable cause = future.cause(); promise.tryFailure(cause); writePromise.setFailure(cause); From 8f01259833a2323da6968b09162854600443ac4d Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Fri, 22 Jun 2018 10:07:08 -0600 Subject: [PATCH 064/417] HpackDecoder treats invalid pseudo-headers as stream level errors Motivation: The HTTP/2 spec dictates that invalid pseudo-headers should cause the request/response to be treated as malformed (8.1.2.1), and the recourse for that is to treat the situation as a stream error of type PROTOCOL_ERROR (8.1.2.6). However, we're treating them as a connection error with the connection being immediately torn down and the HPACK state potentially being corrupted. Modifications: The HpackDecoder now throws a StreamException for validation failures and throwing is deffered until the end of of the decode phase to ensure that the HPACK state isn't corrupted by returning early. Result: Behavior more closely aligned with the HTTP/2 spec. Fixes #8043. --- .../handler/codec/http2/HpackDecoder.java | 83 ++++++++++--------- .../handler/codec/http2/HpackDecoderTest.java | 45 +++++++++- 2 files changed, 87 insertions(+), 41 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java index 078c0798918f..9c680e6a99e0 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java @@ -44,6 +44,7 @@ import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR; import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; import static io.netty.handler.codec.http2.Http2Exception.connectionError; +import static io.netty.handler.codec.http2.Http2Exception.streamError; import static io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName.getPseudoHeader; import static io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName.hasPseudoHeaderFormat; import static io.netty.util.AsciiString.EMPTY_STRING; @@ -119,24 +120,21 @@ final class HpackDecoder { * This method assumes the entire header block is contained in {@code in}. */ public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean validateHeaders) throws Http2Exception { - Http2HeadersSink sink = new Http2HeadersSink(headers, maxHeaderListSize); - decode(in, sink, validateHeaders); + Http2HeadersSink sink = new Http2HeadersSink(streamId, headers, maxHeaderListSize, validateHeaders); + decode(in, sink); - // we have read all of our headers. See if we have exceeded our maxHeaderListSize. We must - // delay throwing until this point to prevent dynamic table corruption - if (sink.exceededMaxLength()) { - headerListSizeExceeded(streamId, maxHeaderListSize, true); - } + // Now that we've read all of our headers we can perform the validation steps. We must + // delay throwing until this point to prevent dynamic table corruption. + sink.finish(); } - private void decode(ByteBuf in, Sink sink, boolean validateHeaders) throws Http2Exception { + private void decode(ByteBuf in, Sink sink) throws Http2Exception { int index = 0; int nameLength = 0; int valueLength = 0; byte state = READ_HEADER_REPRESENTATION; boolean huffmanEncoded = false; CharSequence name = null; - HeaderType headerType = null; IndexType indexType = IndexType.NONE; while (in.isReadable()) { switch (state) { @@ -157,7 +155,6 @@ private void decode(ByteBuf in, Sink sink, boolean validateHeaders) throws Http2 break; default: HpackHeaderField indexedHeader = getIndexedHeader(index); - headerType = validate(indexedHeader.name, headerType, validateHeaders); sink.appendToHeaderList(indexedHeader.name, indexedHeader.value); } } else if ((b & 0x40) == 0x40) { @@ -174,7 +171,6 @@ private void decode(ByteBuf in, Sink sink, boolean validateHeaders) throws Http2 default: // Index was stored as the prefix name = readName(index); - headerType = validate(name, headerType, validateHeaders); nameLength = name.length(); state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; } @@ -201,7 +197,6 @@ private void decode(ByteBuf in, Sink sink, boolean validateHeaders) throws Http2 default: // Index was stored as the prefix name = readName(index); - headerType = validate(name, headerType, validateHeaders); nameLength = name.length(); state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; } @@ -215,7 +210,6 @@ private void decode(ByteBuf in, Sink sink, boolean validateHeaders) throws Http2 case READ_INDEXED_HEADER: HpackHeaderField indexedHeader = getIndexedHeader(decodeULE128(in, index)); - headerType = validate(indexedHeader.name, headerType, validateHeaders); sink.appendToHeaderList(indexedHeader.name, indexedHeader.value); state = READ_HEADER_REPRESENTATION; break; @@ -223,7 +217,6 @@ private void decode(ByteBuf in, Sink sink, boolean validateHeaders) throws Http2 case READ_INDEXED_HEADER_NAME: // Header Name matches an entry in the Header Table name = readName(decodeULE128(in, index)); - headerType = validate(name, headerType, validateHeaders); nameLength = name.length(); state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; break; @@ -254,7 +247,6 @@ private void decode(ByteBuf in, Sink sink, boolean validateHeaders) throws Http2 } name = readStringLiteral(in, nameLength, huffmanEncoded); - headerType = validate(name, headerType, validateHeaders); state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; break; @@ -268,7 +260,6 @@ private void decode(ByteBuf in, Sink sink, boolean validateHeaders) throws Http2 state = READ_LITERAL_HEADER_VALUE_LENGTH; break; case 0: - headerType = validate(name, headerType, validateHeaders); insertHeader(sink, name, EMPTY_STRING, indexType); state = READ_HEADER_REPRESENTATION; break; @@ -293,7 +284,6 @@ private void decode(ByteBuf in, Sink sink, boolean validateHeaders) throws Http2 } CharSequence value = readStringLiteral(in, valueLength, huffmanEncoded); - headerType = validate(name, headerType, validateHeaders); insertHeader(sink, name, value, indexType); state = READ_HEADER_REPRESENTATION; break; @@ -327,7 +317,7 @@ public void setMaxHeaderTableSize(long maxHeaderTableSize) throws Http2Exception } /** - * @deprecated use {@link #setmaxHeaderListSize(long)}; {@code maxHeaderListSizeGoAway} is + * @deprecated use {@link #setMaxHeaderListSize(long)}; {@code maxHeaderListSizeGoAway} is * ignored */ @Deprecated @@ -385,26 +375,23 @@ private void setDynamicTableSize(long dynamicTableSize) throws Http2Exception { hpackDynamicTable.setCapacity(dynamicTableSize); } - private HeaderType validate(CharSequence name, HeaderType previousHeaderType, - final boolean validateHeaders) throws Http2Exception { - if (!validateHeaders) { - return null; - } - + private static HeaderType validate(int streamId, CharSequence name, + HeaderType previousHeaderType) throws Http2Exception { if (hasPseudoHeaderFormat(name)) { if (previousHeaderType == HeaderType.REGULAR_HEADER) { - throw connectionError(PROTOCOL_ERROR, "Pseudo-header field '%s' found after regular header.", name); + throw streamError(streamId, PROTOCOL_ERROR, + "Pseudo-header field '%s' found after regular header.", name); } final Http2Headers.PseudoHeaderName pseudoHeader = getPseudoHeader(name); if (pseudoHeader == null) { - throw connectionError(PROTOCOL_ERROR, "Invalid HTTP/2 pseudo-header '%s' encountered.", name); + throw streamError(streamId, PROTOCOL_ERROR, "Invalid HTTP/2 pseudo-header '%s' encountered.", name); } final HeaderType currentHeaderType = pseudoHeader.isRequestOnly() ? HeaderType.REQUEST_PSEUDO_HEADER : HeaderType.RESPONSE_PSEUDO_HEADER; if (previousHeaderType != null && currentHeaderType != previousHeaderType) { - throw connectionError(PROTOCOL_ERROR, "Mix of request and response pseudo-headers."); + throw streamError(streamId, PROTOCOL_ERROR, "Mix of request and response pseudo-headers."); } return currentHeaderType; @@ -435,8 +422,7 @@ private HpackHeaderField getIndexedHeader(int index) throws Http2Exception { throw INDEX_HEADER_ILLEGAL_INDEX_VALUE; } - private void insertHeader(Sink sink, CharSequence name, CharSequence value, - IndexType indexType) throws Http2Exception { + private void insertHeader(Sink sink, CharSequence name, CharSequence value, IndexType indexType) { sink.appendToHeaderList(name, value); switch (indexType) { @@ -529,32 +515,55 @@ private enum HeaderType { private interface Sink { void appendToHeaderList(CharSequence name, CharSequence value); + void finish() throws Http2Exception; } private static final class Http2HeadersSink implements Sink { private final Http2Headers headers; private final long maxHeaderListSize; + private final int streamId; + private final boolean validate; private long headersLength; private boolean exceededMaxLength; + private HeaderType previousType; + private Http2Exception validationException; - public Http2HeadersSink(Http2Headers headers, long maxHeaderListSize) { + public Http2HeadersSink(int streamId, Http2Headers headers, long maxHeaderListSize, boolean validate) { this.headers = headers; this.maxHeaderListSize = maxHeaderListSize; + this.streamId = streamId; + this.validate = validate; + } + + @Override + public void finish() throws Http2Exception { + if (exceededMaxLength) { + headerListSizeExceeded(streamId, maxHeaderListSize, true); + } else if (validationException != null) { + throw validationException; + } } @Override public void appendToHeaderList(CharSequence name, CharSequence value) { headersLength += HpackHeaderField.sizeOf(name, value); - if (headersLength > maxHeaderListSize) { - exceededMaxLength = true; + exceededMaxLength |= headersLength > maxHeaderListSize; + + if (exceededMaxLength || validationException != null) { + // We don't store the header since we've already failed validation requirements. + return; } - if (!exceededMaxLength) { - headers.add(name, value); + + if (validate) { + try { + previousType = HpackDecoder.validate(streamId, name, previousType); + } catch (Http2Exception ex) { + validationException = ex; + return; + } } - } - public boolean exceededMaxLength() { - return exceededMaxLength; + headers.add(name, value); } } } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDecoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDecoderTest.java index ba0117553ebb..994fef6f37af 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDecoderTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDecoderTest.java @@ -544,7 +544,7 @@ public void unknownPseudoHeader() throws Exception { Http2Headers decoded = new DefaultHttp2Headers(); - expectedException.expect(Http2Exception.class); + expectedException.expect(Http2Exception.StreamException.class); hpackDecoder.decode(1, in, decoded, true); } finally { in.release(); @@ -588,7 +588,7 @@ public void requestPseudoHeaderInResponse() throws Exception { Http2Headers decoded = new DefaultHttp2Headers(); - expectedException.expect(Http2Exception.class); + expectedException.expect(Http2Exception.StreamException.class); hpackDecoder.decode(1, in, decoded, true); } finally { in.release(); @@ -608,7 +608,7 @@ public void responsePseudoHeaderInRequest() throws Exception { Http2Headers decoded = new DefaultHttp2Headers(); - expectedException.expect(Http2Exception.class); + expectedException.expect(Http2Exception.StreamException.class); hpackDecoder.decode(1, in, decoded, true); } finally { in.release(); @@ -628,10 +628,47 @@ public void pseudoHeaderAfterRegularHeader() throws Exception { Http2Headers decoded = new DefaultHttp2Headers(); - expectedException.expect(Http2Exception.class); + expectedException.expect(Http2Exception.StreamException.class); hpackDecoder.decode(1, in, decoded, true); } finally { in.release(); } } + + @Test + public void failedValidationDoesntCorruptHpack() throws Exception { + ByteBuf in1 = Unpooled.buffer(200); + ByteBuf in2 = Unpooled.buffer(200); + try { + HpackEncoder hpackEncoder = new HpackEncoder(true); + + Http2Headers toEncode = new DefaultHttp2Headers(); + toEncode.add(":method", "GET"); + toEncode.add(":status", "200"); + toEncode.add("foo", "bar"); + hpackEncoder.encodeHeaders(1, in1, toEncode, NEVER_SENSITIVE); + + Http2Headers decoded = new DefaultHttp2Headers(); + + try { + hpackDecoder.decode(1, in1, decoded, true); + fail("Should have thrown a StreamException"); + } catch (Http2Exception.StreamException expected) { + assertEquals(1, expected.streamId()); + } + + // Do it again, this time without validation, to make sure the HPACK state is still sane. + decoded.clear(); + hpackEncoder.encodeHeaders(1, in2, toEncode, NEVER_SENSITIVE); + hpackDecoder.decode(1, in2, decoded, false); + + assertEquals(3, decoded.size()); + assertEquals("GET", decoded.method().toString()); + assertEquals("200", decoded.status().toString()); + assertEquals("bar", decoded.get("foo").toString()); + } finally { + in1.release(); + in2.release(); + } + } } From 06f3574e46e0623a45a93d36dbe7aa3d7455a995 Mon Sep 17 00:00:00 2001 From: nickhill Date: Fri, 18 May 2018 09:29:50 -0700 Subject: [PATCH 065/417] Don't calculate max direct memory twice in PlatformDependent Motivation: I'm not sure if trivial changes like this are interesting :-) But I noticed that the PlatformDependent.maxDirectMemory0() method is called twice unnecessarily during static initialization (on the default path at least). Modifications: Use constant MAX_DIRECT_MEMORY already set to the same value instead of calling maxDirectMemory0() again. Result: A surely imperceivable reduction in operations performed at startup. --- .../src/main/java/io/netty/util/internal/PlatformDependent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index e6b082d859bf..d7a8a262185b 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -158,7 +158,7 @@ public Random current() { } else { USE_DIRECT_BUFFER_NO_CLEANER = true; if (maxDirectMemory < 0) { - maxDirectMemory = maxDirectMemory0(); + maxDirectMemory = MAX_DIRECT_MEMORY; if (maxDirectMemory <= 0) { DIRECT_MEMORY_COUNTER = null; } else { From f164759ea3095bea97dc4fe297782b919baaa7c9 Mon Sep 17 00:00:00 2001 From: nickhill Date: Wed, 20 Jun 2018 14:12:44 -0700 Subject: [PATCH 066/417] Support composite buffer creation without array alloc and copy Motivation: Unpooled.unmodifiableBuffer() is currently used to efficiently write arrays of ByteBufs via FixedCompositeByteBuf, but involves an allocation and content-copy of the provided ByteBuf array which in many (most?) cases shouldn't be necessary. Modifications: Modify the internal FixedCompositeByteBuf class to support wrapping the provided ByteBuf array directly. Control this behaviour with a constructor flag and expose the "unsafe" version via a new Unpooled.wrappedUnmodifiableBuffer(ByteBuf...) method. Result: Less garbage on IO paths. I would guess pretty much all existing usage of unmodifiableBuffer() could use the copy-free version but assume it's not safe to change its default behaviour. --- .../netty/buffer/FixedCompositeByteBuf.java | 32 ++++++------------- .../main/java/io/netty/buffer/Unpooled.java | 27 +++++++++++++++- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java index b8d650663f30..93ede04ff1d5 100644 --- a/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java @@ -39,7 +39,7 @@ final class FixedCompositeByteBuf extends AbstractReferenceCountedByteBuf { private final int capacity; private final ByteBufAllocator allocator; private final ByteOrder order; - private final Object[] buffers; + private final ByteBuf[] buffers; private final boolean direct; FixedCompositeByteBuf(ByteBufAllocator allocator, ByteBuf... buffers) { @@ -52,8 +52,7 @@ final class FixedCompositeByteBuf extends AbstractReferenceCountedByteBuf { direct = false; } else { ByteBuf b = buffers[0]; - this.buffers = new Object[buffers.length]; - this.buffers[0] = b; + this.buffers = buffers; boolean direct = true; int nioBufferCount = b.nioBufferCount(); int capacity = b.readableBytes(); @@ -68,7 +67,6 @@ final class FixedCompositeByteBuf extends AbstractReferenceCountedByteBuf { if (!b.isDirect()) { direct = false; } - this.buffers[i] = b; } this.nioBufferCount = nioBufferCount; this.capacity = capacity; @@ -232,20 +230,14 @@ private Component findComponent(int index) { int readable = 0; for (int i = 0 ; i < buffers.length; i++) { Component comp = null; - ByteBuf b; - Object obj = buffers[i]; - boolean isBuffer; - if (obj instanceof ByteBuf) { - b = (ByteBuf) obj; - isBuffer = true; - } else { - comp = (Component) obj; + ByteBuf b = buffers[i]; + if (b instanceof Component) { + comp = (Component) b; b = comp.buf; - isBuffer = false; } readable += b.readableBytes(); if (index < readable) { - if (isBuffer) { + if (comp == null) { // Create a new component and store it in the array so it not create a new object // on the next access. comp = new Component(i, readable - b.readableBytes(), b); @@ -261,11 +253,8 @@ private Component findComponent(int index) { * Return the {@link ByteBuf} stored at the given index of the array. */ private ByteBuf buffer(int i) { - Object obj = buffers[i]; - if (obj instanceof ByteBuf) { - return (ByteBuf) obj; - } - return ((Component) obj).buf; + ByteBuf b = buffers[i]; + return b instanceof Component ? ((Component) b).buf : b; } @Override @@ -684,17 +673,16 @@ public String toString() { return result + ", components=" + buffers.length + ')'; } - private static final class Component { + private static final class Component extends WrappedByteBuf { private final int index; private final int offset; - private final ByteBuf buf; private final int endOffset; Component(int index, int offset, ByteBuf buf) { + super(buf); this.index = index; this.offset = offset; endOffset = offset + buf.readableBytes(); - this.buf = buf; } } } diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/netty/buffer/Unpooled.java index 87559897826e..a48ad0952589 100644 --- a/buffer/src/main/java/io/netty/buffer/Unpooled.java +++ b/buffer/src/main/java/io/netty/buffer/Unpooled.java @@ -22,6 +22,7 @@ import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; @@ -883,7 +884,31 @@ public static ByteBuf unreleasableBuffer(ByteBuf buf) { * not try to slice the given {@link ByteBuf}s to reduce GC-Pressure. */ public static ByteBuf unmodifiableBuffer(ByteBuf... buffers) { - return new FixedCompositeByteBuf(ALLOC, buffers); + return wrappedUnmodifiableBuffer(true, buffers); + } + + /** + * Wrap the given {@link ByteBuf}s in an unmodifiable {@link ByteBuf}. Be aware the returned {@link ByteBuf} will + * not try to slice the given {@link ByteBuf}s to reduce GC-Pressure. + * + * The returned {@link ByteBuf} wraps the provided array directly, and so should not be subsequently modified. + */ + public static ByteBuf wrappedUnmodifiableBuffer(ByteBuf... buffers) { + return wrappedUnmodifiableBuffer(false, buffers); + } + + private static ByteBuf wrappedUnmodifiableBuffer(boolean copy, ByteBuf... buffers) { + switch (buffers.length) { + case 0: + return EMPTY_BUFFER; + case 1: + return buffers[0].asReadOnly(); + default: + if (copy) { + buffers = Arrays.copyOf(buffers, buffers.length, ByteBuf[].class); + } + return new FixedCompositeByteBuf(ALLOC, buffers); + } } private Unpooled() { From a214f2eb9692040cf45e20739a7dd319f47ff8c8 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 26 Jun 2018 07:36:23 +0200 Subject: [PATCH 067/417] Remove id from DnsQueryContextManager whenever the promise is fullfilled. Motivation: We did not handle the case when the query was cancelled which could lead to an exhausted id space. Beside this we did not not cancel the timeout on failed promises. Modifications: - Do the removal of the id from the manager in a FutureListener so its handled in all cases. - Cancel the timeout whenever the original promise was full-filled. Result: Fixes https://github.com/netty/netty/issues/8013. --- .../netty/resolver/dns/DnsQueryContext.java | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java index 3aee7686d31c..1805b85c4b2c 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java @@ -28,6 +28,7 @@ import io.netty.handler.codec.dns.DnsResponse; import io.netty.handler.codec.dns.DnsSection; import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.ScheduledFuture; @@ -39,7 +40,7 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull; -final class DnsQueryContext { +final class DnsQueryContext implements FutureListener> { private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsQueryContext.class); @@ -68,6 +69,9 @@ final class DnsQueryContext { recursionDesired = parent.isRecursionDesired(); id = parent.queryContextManager.add(this); + // Ensure we remove the id from the QueryContextManager once the query completes. + promise.addListener(this); + if (parent.isOptResourceEnabled()) { optResource = new AbstractDnsOptPseudoRrRecord(parent.maxPayloadSize(), 0, 0) { // We may want to remove this in the future and let the user just specify the opt record in the query. @@ -119,9 +123,6 @@ public void operationComplete(Future future) { if (future.isSuccess()) { writeQuery(query, writePromise); } else { - // Remove the id from the manager as we fail the query. - parent.queryContextManager.remove(nameServerAddr(), id); - Throwable cause = future.cause(); promise.tryFailure(cause); writePromise.setFailure(cause); @@ -138,7 +139,7 @@ private void writeQuery(final DnsQuery query, final ChannelPromise writePromise) } else { writeFuture.addListener(new ChannelFutureListener() { @Override - public void operationComplete(ChannelFuture future) throws Exception { + public void operationComplete(ChannelFuture future) { onQueryWriteCompletion(writeFuture); } }); @@ -184,29 +185,18 @@ void finish(AddressedEnvelope envelope } private void setSuccess(AddressedEnvelope envelope) { - parent.queryContextManager.remove(nameServerAddr(), id); - - // Cancel the timeout task. - final ScheduledFuture timeoutFuture = this.timeoutFuture; - if (timeoutFuture != null) { - timeoutFuture.cancel(false); - } - Promise> promise = this.promise; - if (promise.setUncancellable()) { - @SuppressWarnings("unchecked") - AddressedEnvelope castResponse = - (AddressedEnvelope) envelope.retain(); - if (!promise.trySuccess(castResponse)) { - // We failed to notify the promise as it was failed before, thus we need to release the envelope - envelope.release(); - } + @SuppressWarnings("unchecked") + AddressedEnvelope castResponse = + (AddressedEnvelope) envelope.retain(); + if (!promise.trySuccess(castResponse)) { + // We failed to notify the promise as it was failed before, thus we need to release the envelope + envelope.release(); } } private void setFailure(String message, Throwable cause) { final InetSocketAddress nameServerAddr = nameServerAddr(); - parent.queryContextManager.remove(nameServerAddr, id); final StringBuilder buf = new StringBuilder(message.length() + 64); buf.append('[') @@ -225,4 +215,18 @@ private void setFailure(String message, Throwable cause) { } promise.tryFailure(e); } + + @Override + public void operationComplete(Future> future) { + // Cancel the timeout task. + final ScheduledFuture timeoutFuture = this.timeoutFuture; + if (timeoutFuture != null) { + this.timeoutFuture = null; + timeoutFuture.cancel(false); + } + + // Remove the id from the manager as soon as the query completes. This may be because of success, failure or + // cancellation + parent.queryContextManager.remove(nameServerAddr, id); + } } From 05e5ab1ecb98963604d686c1f59b2196cf73e244 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 27 Jun 2018 10:02:25 +0200 Subject: [PATCH 068/417] Store NativeDatagramPacketArray directly in the EpollEventLoop Motivation: We can store the NativeDatagramPacketArray directly in the EpollEventLoop. This removes the need of using FastThreadLocal. Modifications: - Store NativeDatagramPacketArray directly in the EpollEventLoop (just as we do with IovArray as well). Result: Less FastThreadLocal usage and more consistent code. --- .../epoll/AbstractEpollStreamChannel.java | 2 +- .../channel/epoll/EpollDatagramChannel.java | 5 ++- .../netty/channel/epoll/EpollEventLoop.java | 13 +++++- .../epoll/NativeDatagramPacketArray.java | 40 +++++-------------- 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java index ea871db3e193..f1fda0820664 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java @@ -514,7 +514,7 @@ protected int doWriteSingle(ChannelOutboundBuffer in) throws Exception { private int doWriteMultiple(ChannelOutboundBuffer in) throws Exception { final long maxBytesPerGatheringWrite = config().getMaxBytesPerGatheringWrite(); if (PlatformDependent.hasUnsafe()) { - IovArray array = ((EpollEventLoop) eventLoop()).cleanArray(); + IovArray array = ((EpollEventLoop) eventLoop()).cleanIovArray(); array.maxBytes(maxBytesPerGatheringWrite); in.forEachFlushedMessage(array); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java index d9f48464f562..714f612634b1 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java @@ -270,7 +270,8 @@ protected void doWrite(ChannelOutboundBuffer in) throws Exception { try { // Check if sendmmsg(...) is supported which is only the case for GLIBC 2.14+ if (Native.IS_SUPPORTING_SENDMMSG && in.size() > 1) { - NativeDatagramPacketArray array = NativeDatagramPacketArray.getInstance(in); + NativeDatagramPacketArray array = ((EpollEventLoop) eventLoop()).cleanDatagramPacketArray(); + in.forEachFlushedMessage(array); int cnt = array.count(); if (cnt >= 1) { @@ -347,7 +348,7 @@ private boolean doWriteMessage(Object msg) throws Exception { remoteAddress.getAddress(), remoteAddress.getPort()); } } else if (data.nioBufferCount() > 1) { - IovArray array = ((EpollEventLoop) eventLoop()).cleanArray(); + IovArray array = ((EpollEventLoop) eventLoop()).cleanIovArray(); array.add(data); int cnt = array.count(); assert cnt != 0; diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index 913f5edf4291..09439764bcfb 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -62,6 +62,8 @@ final class EpollEventLoop extends SingleThreadEventLoop { private final boolean allowGrowing; private final EpollEventArray events; private final IovArray iovArray = new IovArray(); + private final NativeDatagramPacketArray datagramPacketArray = new NativeDatagramPacketArray(); + private final SelectStrategy selectStrategy; private final IntSupplier selectNowSupplier = new IntSupplier() { @Override @@ -141,11 +143,19 @@ public Integer call() throws Exception { /** * Return a cleared {@link IovArray} that can be used for writes in this {@link EventLoop}. */ - IovArray cleanArray() { + IovArray cleanIovArray() { iovArray.clear(); return iovArray; } + /** + * Return a cleared {@link NativeDatagramPacketArray} that can be used for writes in this {@link EventLoop}. + */ + NativeDatagramPacketArray cleanDatagramPacketArray() { + datagramPacketArray.clear(); + return datagramPacketArray; + } + @Override protected void wakeup(boolean inEventLoop) { if (!inEventLoop && WAKEN_UP_UPDATER.compareAndSet(this, 0, 1)) { @@ -449,6 +459,7 @@ protected void cleanup() { } finally { // release native memory iovArray.release(); + datagramPacketArray.release(); events.free(); } } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java index aafa67ee66d9..c100f3c27a58 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java @@ -19,7 +19,6 @@ import io.netty.channel.ChannelOutboundBuffer; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.unix.IovArray; -import io.netty.util.concurrent.FastThreadLocal; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -32,28 +31,11 @@ */ final class NativeDatagramPacketArray implements ChannelOutboundBuffer.MessageProcessor { - private static final FastThreadLocal ARRAY = - new FastThreadLocal() { - @Override - protected NativeDatagramPacketArray initialValue() throws Exception { - return new NativeDatagramPacketArray(); - } - - @Override - protected void onRemoval(NativeDatagramPacketArray value) throws Exception { - NativeDatagramPacket[] packetsArray = value.packets; - // Release all packets - for (NativeDatagramPacket datagramPacket : packetsArray) { - datagramPacket.release(); - } - } - }; - // Use UIO_MAX_IOV as this is the maximum number we can write with one sendmmsg(...) call. private final NativeDatagramPacket[] packets = new NativeDatagramPacket[UIO_MAX_IOV]; private int count; - private NativeDatagramPacketArray() { + NativeDatagramPacketArray() { for (int i = 0; i < packets.length; i++) { packets[i] = new NativeDatagramPacket(); } @@ -83,7 +65,7 @@ boolean add(DatagramPacket packet) { } @Override - public boolean processMessage(Object msg) throws Exception { + public boolean processMessage(Object msg) { return msg instanceof DatagramPacket && add((DatagramPacket) msg); } @@ -101,15 +83,15 @@ NativeDatagramPacket[] packets() { return packets; } - /** - * Returns a {@link NativeDatagramPacketArray} which is filled with the flushed messages of - * {@link ChannelOutboundBuffer}. - */ - static NativeDatagramPacketArray getInstance(ChannelOutboundBuffer buffer) throws Exception { - NativeDatagramPacketArray array = ARRAY.get(); - array.count = 0; - buffer.forEachFlushedMessage(array); - return array; + void clear() { + this.count = 0; + } + + void release() { + // Release all packets + for (NativeDatagramPacket datagramPacket : packets) { + datagramPacket.release(); + } } /** From d5d1b898d56152d67c81e5cfd722675097d51e73 Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Wed, 27 Jun 2018 13:28:41 -0600 Subject: [PATCH 069/417] Reorder channel state changes in Http2MultiplexCodec child channel Motivation: If a write fails for a Http2MultiplexChannel stream channel, the channel may be forcibly closed, but only after the promise has been failed. That means continuations attached to the promise may see the channel in an inconsistent state of still being open and active. Modifications: Move the satisfaction of the promise to after the channel cleanup logic runs. Result: Listeners attached to the future that resulted in a Failed write will see the stream channel in the correct state. --- .../codec/http2/Http2MultiplexCodec.java | 5 +-- .../codec/http2/Http2MultiplexCodecTest.java | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java index 33b59da13e12..521b897a9ca5 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java @@ -1145,9 +1145,9 @@ private void firstWriteComplete(ChannelFuture future, ChannelPromise promise) { writabilityChanged(Http2MultiplexCodec.this.isWritable(stream)); promise.setSuccess(); } else { - promise.setFailure(wrapStreamClosedError(cause)); // If the first write fails there is not much we can do, just close closeForcibly(); + promise.setFailure(wrapStreamClosedError(cause)); } } @@ -1157,8 +1157,6 @@ private void writeComplete(ChannelFuture future, ChannelPromise promise) { promise.setSuccess(); } else { Throwable error = wrapStreamClosedError(cause); - promise.setFailure(error); - if (error instanceof ClosedChannelException) { if (config.isAutoClose()) { // Close channel if needed. @@ -1167,6 +1165,7 @@ private void writeComplete(ChannelFuture future, ChannelPromise promise) { outboundClosed = true; } } + promise.setFailure(error); } } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java index c272b9a89859..314bef7c23ee 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java @@ -33,9 +33,12 @@ import java.net.InetSocketAddress; import java.nio.channels.ClosedChannelException; +import java.util.ArrayDeque; +import java.util.Queue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import io.netty.util.ReferenceCountUtil; import org.junit.After; import org.junit.Before; import org.junit.Ignore; @@ -464,6 +467,7 @@ public void operationComplete(ChannelFuture future) { childChannel.close(p).syncUninterruptibly(); assertFalse(channelOpen.get()); + assertFalse(channelActive.get()); assertFalse(childChannel.isActive()); } @@ -488,6 +492,46 @@ public void operationComplete(ChannelFuture future) { childChannel.close().syncUninterruptibly(); assertFalse(channelOpen.get()); + assertFalse(channelActive.get()); + assertFalse(childChannel.isActive()); + } + + @Test + public void channelClosedWhenWriteFutureFails() { + final Queue writePromises = new ArrayDeque(); + writer = new Writer() { + @Override + void write(Object msg, ChannelPromise promise) { + ReferenceCountUtil.release(msg); + writePromises.offer(promise); + } + }; + + LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); + Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + + assertTrue(childChannel.isOpen()); + assertTrue(childChannel.isActive()); + + final AtomicBoolean channelOpen = new AtomicBoolean(true); + final AtomicBoolean channelActive = new AtomicBoolean(true); + + ChannelFuture f = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); + assertFalse(f.isDone()); + f.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + channelOpen.set(future.channel().isOpen()); + channelActive.set(future.channel().isActive()); + } + }); + + ChannelPromise first = writePromises.poll(); + first.setFailure(new ClosedChannelException()); + f.awaitUninterruptibly(); + + assertFalse(channelOpen.get()); + assertFalse(channelActive.get()); assertFalse(childChannel.isActive()); } From 34b25dc94caf9899f5d1955263c0deef910f7df7 Mon Sep 17 00:00:00 2001 From: Tyler Rockwood Date: Wed, 27 Jun 2018 23:12:26 -0700 Subject: [PATCH 070/417] Expose channel count for FixedChannelPool (#8059) Motivation: We use FixedChannelPool in production, and we believe we have a leak that doesn't return sockets to the pool (but they should be closed), thus blocking us from creating new connections when we need them. I haven't confirmed this yet, but right now I have to resort to reflection to access this field which makes me sad. Modification: Expose the acquiredChannelCount field through a getter method. Result: Allows introspection of the pool size in FixedChannelPool. --- .../netty/channel/pool/FixedChannelPool.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java b/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java index 16a8a7cc9a7b..5ca376f88d21 100644 --- a/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java +++ b/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java @@ -28,6 +28,7 @@ import java.nio.channels.ClosedChannelException; import java.util.ArrayDeque; import java.util.Queue; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -70,7 +71,7 @@ public enum AcquireTimeoutAction { private final Queue pendingAcquireQueue = new ArrayDeque(); private final int maxConnections; private final int maxPendingAcquires; - private int acquiredChannelCount; + private final AtomicInteger acquiredChannelCount = new AtomicInteger(); private int pendingAcquireCount; private boolean closed; @@ -229,6 +230,11 @@ public void onTimeout(AcquireTask task) { this.maxPendingAcquires = maxPendingAcquires; } + /** Returns the number of acquired channels that this pool thinks it has. */ + public int acquiredChannelCount() { + return acquiredChannelCount.get(); + } + @Override public Future acquire(final Promise promise) { try { @@ -255,8 +261,8 @@ private void acquire0(final Promise promise) { promise.setFailure(POOL_CLOSED_ON_ACQUIRE_EXCEPTION); return; } - if (acquiredChannelCount < maxConnections) { - assert acquiredChannelCount >= 0; + if (acquiredChannelCount.get() < maxConnections) { + assert acquiredChannelCount.get() >= 0; // We need to create a new promise as we need to ensure the AcquireListener runs in the correct // EventLoop @@ -319,10 +325,9 @@ public void operationComplete(Future future) throws Exception { } private void decrementAndRunTaskQueue() { - --acquiredChannelCount; - // We should never have a negative value. - assert acquiredChannelCount >= 0; + int currentCount = acquiredChannelCount.decrementAndGet(); + assert currentCount >= 0; // Run the pending acquire tasks before notify the original promise so if the user would // try to acquire again from the ChannelFutureListener and the pendingAcquireCount is >= @@ -332,7 +337,7 @@ private void decrementAndRunTaskQueue() { } private void runTaskQueue() { - while (acquiredChannelCount < maxConnections) { + while (acquiredChannelCount.get() < maxConnections) { AcquireTask task = pendingAcquireQueue.poll(); if (task == null) { break; @@ -352,7 +357,7 @@ private void runTaskQueue() { // We should never have a negative value. assert pendingAcquireCount >= 0; - assert acquiredChannelCount >= 0; + assert acquiredChannelCount.get() >= 0; } // AcquireTask extends AcquireListener to reduce object creations and so GC pressure @@ -431,7 +436,7 @@ public void acquired() { if (acquired) { return; } - acquiredChannelCount++; + acquiredChannelCount.incrementAndGet(); acquired = true; } } @@ -464,7 +469,7 @@ private void close0() { } task.promise.setFailure(new ClosedChannelException()); } - acquiredChannelCount = 0; + acquiredChannelCount.set(0); pendingAcquireCount = 0; // Ensure we dispatch this on another Thread as close0 will be called from the EventExecutor and we need From 28187300924604b44c24487a0c5e76dc07757597 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 28 Jun 2018 08:13:52 +0200 Subject: [PATCH 071/417] OpenSSL (and so netty-tcnative) should allow to use custom engine. (#8050) Motivation: OpenSSL allows to use a custom engine for its cryptographic operations. We should allow the user to make use of it if needed. See also: https://www.openssl.org/docs/man1.0.2/crypto/engine.html. Modifications: Add new system property which can be used to specify the engine to use (null is the default and will use the build in default impl). Result: More flexible way of using OpenSSL. --- .../src/main/java/io/netty/handler/ssl/OpenSsl.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index 27753d2284d5..4b031291e695 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -105,7 +105,13 @@ public final class OpenSsl { } try { - initializeTcNative(); + String engine = SystemPropertyUtil.get("io.netty.handler.ssl.openssl.engine", null); + if (engine == null) { + logger.debug("Initialize netty-tcnative using engine: 'default'"); + } else { + logger.debug("Initialize netty-tcnative using engine: '{}'", engine); + } + initializeTcNative(engine); // The library was initialized successfully. If loading the library failed above, // reset the cause now since it appears that the library was loaded by some other @@ -435,8 +441,8 @@ private static void loadTcNative() throws Exception { libNames.toArray(new String[libNames.size()])); } - private static boolean initializeTcNative() throws Exception { - return Library.initialize(); + private static boolean initializeTcNative(String engine) throws Exception { + return Library.initialize("provided", engine); } static void releaseIfNeeded(ReferenceCounted counted) { From 5b1fe611a637c362a60b391079fff73b1a4ef912 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 28 Jun 2018 08:15:27 +0200 Subject: [PATCH 072/417] Remove usage of ObjectCleaner (#8064) Motivation: ObjectCleaner does start a Thread to handle the cleaning of resources which leaks into the users application. We should not use it in netty itself to make things more predictable. Modifications: - Remove usage of ObjectCleaner and use finalize as a replacement when possible. - Clarify javadocs for FastThreadLocal.onRemoval(...) to ensure its clear that remove() is not guaranteed to be called when the Thread completees and so this method is not enough to guarantee cleanup for this case. Result: Fixes https://github.com/netty/netty/issues/8017. --- .../java/io/netty/buffer/PoolThreadCache.java | 10 ++++++ .../src/main/java/io/netty/util/Recycler.java | 33 ++++++++++--------- .../util/concurrent/FastThreadLocal.java | 8 +++-- .../util/concurrent/FastThreadLocalTest.java | 11 ++++--- 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java index 3503748c0d9b..3279da73d9e1 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java +++ b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java @@ -219,6 +219,16 @@ private MemoryRegionCache cache(PoolArena area, int normCapacity, SizeClas } } + /// TODO: In the future when we move to Java9+ we should use java.lang.ref.Cleaner. + @Override + protected void finalize() throws Throwable { + try { + super.finalize(); + } finally { + free(); + } + } + /** * Should be called if the Thread that uses this cache is about to exist to release resources out of the cache */ diff --git a/common/src/main/java/io/netty/util/Recycler.java b/common/src/main/java/io/netty/util/Recycler.java index e84adc5a9a68..491464d9a315 100644 --- a/common/src/main/java/io/netty/util/Recycler.java +++ b/common/src/main/java/io/netty/util/Recycler.java @@ -17,7 +17,6 @@ package io.netty.util; import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.internal.ObjectCleaner; import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -244,10 +243,9 @@ static final class Link extends AtomicInteger { Link next; } - // This act as a place holder for the head Link but also will be used by the ObjectCleaner - // to return space that was before reserved. Its important this does not hold any reference to - // either Stack or WeakOrderQueue. - static final class Head implements Runnable { + // This act as a place holder for the head Link but also will reclaim space once finalized. + // Its important this does not hold any reference to either Stack or WeakOrderQueue. + static final class Head { private final AtomicInteger availableSharedCapacity; Link link; @@ -256,12 +254,21 @@ static final class Head implements Runnable { this.availableSharedCapacity = availableSharedCapacity; } + /// TODO: In the future when we move to Java9+ we should use java.lang.ref.Cleaner. @Override - public void run() { - Link head = link; - while (head != null) { - reclaimSpace(LINK_CAPACITY); - head = head.next; + protected void finalize() throws Throwable { + try { + super.finalize(); + } finally { + Link head = link; + link = null; + while (head != null) { + reclaimSpace(LINK_CAPACITY); + Link next = head.next; + // Unlink to help GC and guard against GC nepotism. + head.next = null; + head = next; + } } } @@ -318,12 +325,6 @@ static WeakOrderQueue newQueue(Stack stack, Thread thread) { // may be accessed while its still constructed. stack.setHead(queue); - // We need to reclaim all space that was reserved by this WeakOrderQueue so we not run out of space in - // the stack. This is needed as we not have a good life-time control over the queue as it is used in a - // WeakHashMap which will drop it at any time. - final Head head = queue.head; - ObjectCleaner.register(queue, head); - return queue; } diff --git a/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java b/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java index 561058abca07..dacaa141008e 100644 --- a/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java +++ b/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java @@ -16,7 +16,6 @@ package io.netty.util.concurrent; import io.netty.util.internal.InternalThreadLocalMap; -import io.netty.util.internal.ObjectCleaner; import io.netty.util.internal.PlatformDependent; import java.util.Collections; @@ -153,6 +152,8 @@ private void registerCleaner(final InternalThreadLocalMap threadLocalMap) { threadLocalMap.setCleanerFlag(index); + // TODO: We need to find a better way to handle this. + /* // We will need to ensure we will trigger remove(InternalThreadLocalMap) so everything will be released // and FastThreadLocal.onRemoval(...) will be called. ObjectCleaner.register(current, new Runnable() { @@ -164,6 +165,7 @@ public void run() { // the Thread is collected by GC. In this case the ThreadLocal will be gone away already. } }); + */ } /** @@ -281,7 +283,9 @@ protected V initialValue() throws Exception { } /** - * Invoked when this thread local variable is removed by {@link #remove()}. + * Invoked when this thread local variable is removed by {@link #remove()}. Be aware that {@link #remove()} + * is not guaranteed to be called when the `Thread` completes which means you can not depend on this for + * cleanup of the resources in the case of `Thread` completion. */ protected void onRemoval(@SuppressWarnings("UnusedParameters") V value) throws Exception { } } diff --git a/common/src/test/java/io/netty/util/concurrent/FastThreadLocalTest.java b/common/src/test/java/io/netty/util/concurrent/FastThreadLocalTest.java index 4551d4f78b92..6457de297a11 100644 --- a/common/src/test/java/io/netty/util/concurrent/FastThreadLocalTest.java +++ b/common/src/test/java/io/netty/util/concurrent/FastThreadLocalTest.java @@ -18,6 +18,7 @@ import io.netty.util.internal.ObjectCleaner; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.concurrent.atomic.AtomicBoolean; @@ -96,13 +97,13 @@ public void run() { thread.start(); thread.join(); - assertEquals(1, ObjectCleaner.getLiveSetCount() - sizeWhenStart); + assertEquals(0, ObjectCleaner.getLiveSetCount() - sizeWhenStart); Thread thread2 = new Thread(runnable); thread2.start(); thread2.join(); - assertEquals(2, ObjectCleaner.getLiveSetCount() - sizeWhenStart); + assertEquals(0, ObjectCleaner.getLiveSetCount() - sizeWhenStart); } @Test @@ -128,13 +129,13 @@ public void run() { thread.start(); thread.join(); - assertEquals(2, ObjectCleaner.getLiveSetCount() - sizeWhenStart); + assertEquals(0, ObjectCleaner.getLiveSetCount() - sizeWhenStart); Thread thread2 = new Thread(runnable); thread2.start(); thread2.join(); - assertEquals(4, ObjectCleaner.getLiveSetCount() - sizeWhenStart); + assertEquals(0, ObjectCleaner.getLiveSetCount() - sizeWhenStart); } @Test(timeout = 4000) @@ -142,6 +143,7 @@ public void testOnRemoveCalledForFastThreadLocalGet() throws Exception { testOnRemoveCalled(true, true); } + @Ignore("onRemoval(...) not called with non FastThreadLocal") @Test(timeout = 4000) public void testOnRemoveCalledForNonFastThreadLocalGet() throws Exception { testOnRemoveCalled(false, true); @@ -152,6 +154,7 @@ public void testOnRemoveCalledForFastThreadLocalSet() throws Exception { testOnRemoveCalled(true, false); } + @Ignore("onRemoval(...) not called with non FastThreadLocal") @Test(timeout = 4000) public void testOnRemoveCalledForNonFastThreadLocalSet() throws Exception { testOnRemoveCalled(false, false); From 9bf74a680938296e1314791772926b19c802ff2d Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 28 Jun 2018 08:42:44 +0200 Subject: [PATCH 073/417] Update to netty-tcnative 2.0.12.Final (#8073) Motivation: A new version of tcnative was released that allows to use features depending on the runtime version of openssl, which makes it possible to use KeyManagerFactory and hostname verification on newer versions of centos/fedora/rhel and debian/ubuntu without the need to compile again. Modifications: Update to 2.0.12.Final Result: Use latest version of netty-tcnative to support more features. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a3a1622dcd6e..6523acbe0e0e 100644 --- a/pom.xml +++ b/pom.xml @@ -221,7 +221,7 @@ fedora netty-tcnative - 2.0.11.Final + 2.0.12.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber From ecc238bea5987fb7e71710230c923ab6a6dba37a Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 28 Jun 2018 11:07:13 +0200 Subject: [PATCH 074/417] Only try to call SSL.setHostnameValidation(...) if needed. (#8074) Motivation: As the used OpenSSL version may not support hostname validation we should only really call SSL.setHostNameValidation(...) if we detect that its needed. Modifications: Only call SSL.setHostNameValidation if it was disabled before and now it needs to be enabled or if it was enabled before and it should be disabled now. Result: Less risk of an exception when using an OpenSSL version that does not support hostname validation. --- .../ssl/ReferenceCountedOpenSslEngine.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 658bb015585e..94d45d109ba2 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -1783,10 +1783,20 @@ public final synchronized void setSSLParameters(SSLParameters sslParameters) { } final String endPointIdentificationAlgorithm = sslParameters.getEndpointIdentificationAlgorithm(); - final boolean endPointVerificationEnabled = endPointIdentificationAlgorithm != null && - !endPointIdentificationAlgorithm.isEmpty(); - SSL.setHostNameValidation(ssl, DEFAULT_HOSTNAME_VALIDATION_FLAGS, - endPointVerificationEnabled ? getPeerHost() : null); + final boolean endPointVerificationEnabled = isEndPointVerificationEnabled(endPointIdentificationAlgorithm); + + final boolean wasEndPointVerificationEnabled = + isEndPointVerificationEnabled(this.endPointIdentificationAlgorithm); + + if (wasEndPointVerificationEnabled && !endPointVerificationEnabled) { + // Passing in null will disable hostname verification again so only do so if it was enabled before. + SSL.setHostNameValidation(ssl, DEFAULT_HOSTNAME_VALIDATION_FLAGS, null); + } else { + String host = endPointVerificationEnabled ? getPeerHost() : null; + if (host != null && !host.isEmpty()) { + SSL.setHostNameValidation(ssl, DEFAULT_HOSTNAME_VALIDATION_FLAGS, host); + } + } // If the user asks for hostname verification we must ensure we verify the peer. // If the user disables hostname verification we leave it up to the user to change the mode manually. if (clientMode && endPointVerificationEnabled) { @@ -1799,6 +1809,10 @@ public final synchronized void setSSLParameters(SSLParameters sslParameters) { super.setSSLParameters(sslParameters); } + private static boolean isEndPointVerificationEnabled(String endPointIdentificationAlgorithm) { + return endPointIdentificationAlgorithm != null && !endPointIdentificationAlgorithm.isEmpty(); + } + private boolean isDestroyed() { return destroyed != 0; } From c321e8ea4a1349125510bd9156a4e9a4c3e32741 Mon Sep 17 00:00:00 2001 From: Scott Mitchell Date: Thu, 28 Jun 2018 11:33:16 -0700 Subject: [PATCH 075/417] HTTP/2 outbound event after receiving go_away forces sending a go_away (#8069) Motivation: If the local endpoint receives a GO_AWAY frame and then tries to write a stream with a streamId higher than the last know stream ID we will throw a connection error. This results in the local peer sending a GO_AWAY frame to the remote peer, but this is not necessary as the error can be isolated to the local endpoint and communicated via the ChannelFuture return value. Modifications: - Instead of throwing a connection error, throw a stream error that simulates the peer receiving the stream and replying with a RST Result: Connections are not closed abruptly when trying to create a stream on the local endpoint after a GO_AWAY frame is received. --- .../codec/http2/DefaultHttp2Connection.java | 6 +- .../codec/http2/Http2ConnectionHandler.java | 4 +- .../http2/Http2ConnectionRoundtripTest.java | 120 +++++++++++++++++- 3 files changed, 120 insertions(+), 10 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java index 425131745726..8dd0a31cd28f 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java @@ -864,9 +864,9 @@ private void updateMaxStreams() { private void checkNewStreamAllowed(int streamId, State state) throws Http2Exception { assert state != IDLE; if (goAwayReceived() && streamId > localEndpoint.lastStreamKnownByPeer()) { - throw connectionError(PROTOCOL_ERROR, "Cannot create stream %d since this endpoint has received a " + - "GOAWAY frame with last stream id %d.", streamId, - localEndpoint.lastStreamKnownByPeer()); + throw streamError(streamId, REFUSED_STREAM, + "Cannot create stream %d since this endpoint has received a GOAWAY frame with last stream id %d.", + streamId, localEndpoint.lastStreamKnownByPeer()); } if (!isValidStreamId(streamId)) { if (streamId < 0) { diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java index 29f9def87e2c..e44149de5bd6 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java @@ -701,7 +701,9 @@ protected void onStreamError(ChannelHandlerContext ctx, boolean outbound, } if (stream == null) { - resetUnknownStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise()); + if (!outbound || connection().local().mayHaveCreatedStream(streamId)) { + resetUnknownStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise()); + } } else { resetStream(ctx, stream, http2Ex.error().code(), ctx.newPromise()); } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java index 9860d28fee90..a909b9eb1c9f 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java @@ -52,11 +52,14 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicReference; +import static io.netty.buffer.Unpooled.EMPTY_BUFFER; import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; +import static io.netty.handler.codec.http2.Http2Error.NO_ERROR; import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; import static io.netty.handler.codec.http2.Http2TestUtil.randomString; import static io.netty.handler.codec.http2.Http2TestUtil.runInChannel; +import static java.lang.Integer.MAX_VALUE; import static java.util.concurrent.TimeUnit.SECONDS; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.not; @@ -220,7 +223,7 @@ public void run() throws Http2Exception { anyLong()); // The server will not respond, and so don't wait for graceful shutdown - http2Client.gracefulShutdownTimeoutMillis(0); + setClientGracefulShutdownTime(0); } @Test @@ -766,7 +769,7 @@ public void handlerAdded(ChannelHandlerContext ctx) throws Exception { assertTrue(clientChannel.isOpen()); // Set the timeout very low because we know graceful shutdown won't complete - http2Client.gracefulShutdownTimeoutMillis(0); + setClientGracefulShutdownTime(0); } @Test @@ -774,7 +777,7 @@ public void noMoreStreamIdsShouldSendGoAway() throws Exception { bootstrapEnv(1, 1, 3, 1, 1); // Don't wait for the server to close streams - http2Client.gracefulShutdownTimeoutMillis(0); + setClientGracefulShutdownTime(0); // Create a single stream by sending a HEADERS frame to the server. final Http2Headers headers = dummyHeaders(); @@ -792,7 +795,7 @@ public void run() throws Http2Exception { runInChannel(clientChannel, new Http2Runnable() { @Override public void run() throws Http2Exception { - http2Client.encoder().writeHeaders(ctx(), Integer.MAX_VALUE + 1, headers, 0, (short) 16, false, 0, + http2Client.encoder().writeHeaders(ctx(), MAX_VALUE + 1, headers, 0, (short) 16, false, 0, true, newPromise()); http2Client.flush(ctx()); } @@ -803,6 +806,89 @@ public void run() throws Http2Exception { eq(PROTOCOL_ERROR.code()), any(ByteBuf.class)); } + @Test + public void createStreamAfterReceiveGoAwayShouldNotSendGoAway() throws Exception { + bootstrapEnv(1, 1, 2, 1, 1); + + // We want both sides to do graceful shutdown during the test. + setClientGracefulShutdownTime(10000); + setServerGracefulShutdownTime(10000); + + final CountDownLatch clientGoAwayLatch = new CountDownLatch(1); + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocationOnMock) throws Throwable { + clientGoAwayLatch.countDown(); + return null; + } + }).when(clientListener).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class)); + + // Create a single stream by sending a HEADERS frame to the server. + final Http2Headers headers = dummyHeaders(); + runInChannel(clientChannel, new Http2Runnable() { + @Override + public void run() throws Http2Exception { + http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, + false, newPromise()); + http2Client.flush(ctx()); + } + }); + + assertTrue(serverSettingsAckLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); + + // Server has received the headers, so the stream is open + assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); + + runInChannel(serverChannel, new Http2Runnable() { + @Override + public void run() throws Http2Exception { + http2Server.encoder().writeGoAway(serverCtx(), 3, NO_ERROR.code(), EMPTY_BUFFER, serverNewPromise()); + http2Server.flush(serverCtx()); + } + }); + + // wait for the client to receive the GO_AWAY. + assertTrue(clientGoAwayLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); + verify(clientListener).onGoAwayRead(any(ChannelHandlerContext.class), eq(3), eq(NO_ERROR.code()), + any(ByteBuf.class)); + + final AtomicReference clientWriteAfterGoAwayFutureRef = new AtomicReference(); + final CountDownLatch clientWriteAfterGoAwayLatch = new CountDownLatch(1); + runInChannel(clientChannel, new Http2Runnable() { + @Override + public void run() throws Http2Exception { + ChannelFuture f = http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0, + true, newPromise()); + clientWriteAfterGoAwayFutureRef.set(f); + http2Client.flush(ctx()); + f.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + clientWriteAfterGoAwayLatch.countDown(); + } + }); + } + }); + + // Wait for the client's write operation to complete. + assertTrue(clientWriteAfterGoAwayLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); + + ChannelFuture clientWriteAfterGoAwayFuture = clientWriteAfterGoAwayFutureRef.get(); + assertNotNull(clientWriteAfterGoAwayFuture); + Throwable clientCause = clientWriteAfterGoAwayFuture.cause(); + assertThat(clientCause, is(instanceOf(Http2Exception.StreamException.class))); + assertEquals(Http2Error.REFUSED_STREAM.code(), ((Http2Exception.StreamException) clientCause).error().code()); + + // Wait for the server to receive a GO_AWAY, but this is expected to timeout! + assertFalse(goAwayLatch.await(1, SECONDS)); + verify(serverListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), + any(ByteBuf.class)); + + // Shutdown shouldn't wait for the server to close streams + setClientGracefulShutdownTime(0); + setServerGracefulShutdownTime(0); + } + @Test public void flowControlProperlyChunksLargeMessage() throws Exception { final Http2Headers headers = dummyHeaders(); @@ -861,7 +947,7 @@ public void run() throws Http2Exception { assertArrayEquals(data.array(), received); } finally { // Don't wait for server to close streams - http2Client.gracefulShutdownTimeoutMillis(0); + setClientGracefulShutdownTime(0); data.release(); out.close(); } @@ -949,7 +1035,7 @@ public void run() throws Http2Exception { } } finally { // Don't wait for server to close streams - http2Client.gracefulShutdownTimeoutMillis(0); + setClientGracefulShutdownTime(0); data.release(); } } @@ -1063,6 +1149,28 @@ public Integer answer(InvocationOnMock invocation) throws Throwable { any(ByteBuf.class), anyInt(), anyBoolean()); } + private void setClientGracefulShutdownTime(final long millis) throws InterruptedException { + setGracefulShutdownTime(clientChannel, http2Client, millis); + } + + private void setServerGracefulShutdownTime(final long millis) throws InterruptedException { + setGracefulShutdownTime(serverChannel, http2Server, millis); + } + + private static void setGracefulShutdownTime(Channel channel, final Http2ConnectionHandler handler, + final long millis) throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + runInChannel(channel, new Http2Runnable() { + @Override + public void run() throws Http2Exception { + handler.gracefulShutdownTimeoutMillis(millis); + latch.countDown(); + } + }); + + assertTrue(latch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); + } + /** * Creates a {@link ByteBuf} of the given length, filled with random bytes. */ From 83710cb2e1060d37ba7bac90afbaea15e9db11ed Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 29 Jun 2018 07:56:04 +0200 Subject: [PATCH 076/417] Replace toArray(new T[size]) with toArray(new T[0]) to eliminate zero-out and allow the VM to optimize. (#8075) Motivation: Using toArray(new T[0]) is usually the faster aproach these days. We should use it. See also https://shipilev.net/blog/2016/arrays-wisdom-ancients/#_conclusion. Modifications: Replace toArray(new T[size]) with toArray(new T[0]). Result: Faster code. --- .../main/java/io/netty/buffer/CompositeByteBuf.java | 4 ++-- .../java/io/netty/buffer/FixedCompositeByteBuf.java | 2 +- .../io/netty/buffer/AbstractCompositeByteBufTest.java | 2 +- .../handler/codec/http/cookie/ClientCookieEncoder.java | 4 ++-- .../multipart/HttpPostMultipartRequestDecoder.java | 2 +- .../handler/codec/http/DefaultHttpHeadersTest.java | 2 +- common/src/main/java/io/netty/util/AsciiString.java | 2 +- .../concurrent/AbstractScheduledEventExecutor.java | 2 +- .../java/io/netty/util/concurrent/FastThreadLocal.java | 2 +- .../src/test/java/io/netty/util/ConstantPoolTest.java | 2 +- .../io/netty/handler/ssl/ConscryptAlpnSslEngine.java | 2 +- .../netty/handler/ssl/IdentityCipherSuiteFilter.java | 6 +++--- .../main/java/io/netty/handler/ssl/JdkSslContext.java | 2 +- .../src/main/java/io/netty/handler/ssl/OpenSsl.java | 2 +- .../src/main/java/io/netty/handler/ssl/PemReader.java | 2 +- .../ssl/ReferenceCountedOpenSslClientContext.java | 2 +- .../handler/ssl/ReferenceCountedOpenSslContext.java | 2 +- .../handler/ssl/ReferenceCountedOpenSslEngine.java | 10 +++++----- .../netty/handler/ssl/SupportedCipherSuiteFilter.java | 2 +- .../ssl/util/FingerprintTrustManagerFactory.java | 4 ++-- .../java/io/netty/handler/ssl/OpenSslEngineTest.java | 2 +- .../microbench/util/AbstractMicrobenchmarkBase.java | 3 +-- .../dns/DefaultDnsServerAddressStreamProvider.java | 2 +- .../java/io/netty/resolver/dns/DnsNameResolver.java | 2 +- .../io/netty/resolver/dns/DnsNameResolverBuilder.java | 2 +- .../java/io/netty/resolver/dns/DnsServerAddresses.java | 4 ++-- .../test/java/io/netty/resolver/dns/TestDnsServer.java | 2 +- .../java/io/netty/resolver/AddressResolverGroup.java | 2 +- .../java/io/netty/test/udt/util/CaliperMeasure.java | 2 +- .../main/java/io/netty/bootstrap/ServerBootstrap.java | 4 ++-- 30 files changed, 41 insertions(+), 42 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index bcd5f0a56483..3d6fd215b937 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -391,7 +391,7 @@ private int addComponents0(boolean increaseIndex, int cIndex, Iterable } Collection col = (Collection) buffers; - return addComponents0(increaseIndex, cIndex, col.toArray(new ByteBuf[col.size()]), 0 , col.size()); + return addComponents0(increaseIndex, cIndex, col.toArray(new ByteBuf[0]), 0 , col.size()); } /** @@ -1507,7 +1507,7 @@ public ByteBuffer[] nioBuffers(int index, int length) { i ++; } - return buffers.toArray(new ByteBuffer[buffers.size()]); + return buffers.toArray(new ByteBuffer[0]); } /** diff --git a/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java index 93ede04ff1d5..08c8d7fc6a5c 100644 --- a/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java @@ -593,7 +593,7 @@ public ByteBuffer[] nioBuffers(int index, int length) { s = buffer(++i); } - return array.toArray(new ByteBuffer[array.size()]); + return array.toArray(new ByteBuffer[0]); } finally { array.recycle(); } diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java index f51475bf9ae6..22ca0546aa99 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java @@ -87,7 +87,7 @@ protected ByteBuf newBuffer(int length, int maxCapacity) { buffers.add(EMPTY_BUFFER); } - ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[buffers.size()])).order(order); + ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[0])).order(order); // Truncate to the requested capacity. buffer.capacity(length); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java index 9554b64ed462..0a66aaad7a2f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java @@ -161,7 +161,7 @@ public String encode(Collection cookies) { if (cookies.size() == 1) { encode(buf, cookies.iterator().next()); } else { - Cookie[] cookiesSorted = cookies.toArray(new Cookie[cookies.size()]); + Cookie[] cookiesSorted = cookies.toArray(new Cookie[0]); Arrays.sort(cookiesSorted, COOKIE_COMPARATOR); for (Cookie c : cookiesSorted) { encode(buf, c); @@ -198,7 +198,7 @@ public String encode(Iterable cookies) { while (cookiesIt.hasNext()) { cookiesList.add(cookiesIt.next()); } - Cookie[] cookiesSorted = cookiesList.toArray(new Cookie[cookiesList.size()]); + Cookie[] cookiesSorted = cookiesList.toArray(new Cookie[0]); Arrays.sort(cookiesSorted, COOKIE_COMPARATOR); for (Cookie c : cookiesSorted) { encode(buf, c); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java index 8e3a90c009ba..4fefc1139822 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java @@ -1515,6 +1515,6 @@ private static String[] splitMultipartHeaderValues(String svalue) { } } values.add(svalue.substring(start)); - return values.toArray(new String[values.size()]); + return values.toArray(new String[0]); } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpHeadersTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpHeadersTest.java index 3f2a678ab525..d0f6c414f463 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpHeadersTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpHeadersTest.java @@ -232,7 +232,7 @@ public void providesHeaderNamesAsArray() throws Exception { .add(HttpHeaderNames.CONTENT_LENGTH, 10) .names(); - String[] namesArray = nettyHeaders.toArray(new String[nettyHeaders.size()]); + String[] namesArray = nettyHeaders.toArray(new String[0]); assertArrayEquals(namesArray, new String[] { HttpHeaderNames.CONTENT_LENGTH.toString() }); } diff --git a/common/src/main/java/io/netty/util/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java index eca0288c2ea8..58a6f45a9df3 100644 --- a/common/src/main/java/io/netty/util/AsciiString.java +++ b/common/src/main/java/io/netty/util/AsciiString.java @@ -1115,7 +1115,7 @@ public AsciiString[] split(char delim) { } } - return res.toArray(new AsciiString[res.size()]); + return res.toArray(new AsciiString[0]); } /** diff --git a/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java index 6585cd84f548..41430c708c3d 100644 --- a/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java +++ b/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java @@ -78,7 +78,7 @@ protected void cancelScheduledTasks() { } final ScheduledFutureTask[] scheduledTasks = - scheduledTaskQueue.toArray(new ScheduledFutureTask[scheduledTaskQueue.size()]); + scheduledTaskQueue.toArray(new ScheduledFutureTask[0]); for (ScheduledFutureTask task: scheduledTasks) { task.cancelWithoutRemove(false); diff --git a/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java b/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java index dacaa141008e..9d808c7f45d5 100644 --- a/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java +++ b/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java @@ -62,7 +62,7 @@ public static void removeAll() { @SuppressWarnings("unchecked") Set> variablesToRemove = (Set>) v; FastThreadLocal[] variablesToRemoveArray = - variablesToRemove.toArray(new FastThreadLocal[variablesToRemove.size()]); + variablesToRemove.toArray(new FastThreadLocal[0]); for (FastThreadLocal tlv: variablesToRemoveArray) { tlv.remove(threadLocalMap); } diff --git a/common/src/test/java/io/netty/util/ConstantPoolTest.java b/common/src/test/java/io/netty/util/ConstantPoolTest.java index d8b1bd352b79..a3fc4e7c1f92 100644 --- a/common/src/test/java/io/netty/util/ConstantPoolTest.java +++ b/common/src/test/java/io/netty/util/ConstantPoolTest.java @@ -75,7 +75,7 @@ public void testCompare() { set.add(d); set.add(a); - TestConstant[] array = set.toArray(new TestConstant[5]); + TestConstant[] array = set.toArray(new TestConstant[0]); assertThat(array.length, is(5)); // Sort by name diff --git a/handler/src/main/java/io/netty/handler/ssl/ConscryptAlpnSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ConscryptAlpnSslEngine.java index b13dd7566c88..d9767a710627 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ConscryptAlpnSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ConscryptAlpnSslEngine.java @@ -71,7 +71,7 @@ private ConscryptAlpnSslEngine(SSLEngine engine, ByteBufAllocator alloc, List ciphers, List defaul Set supportedCiphers) { if (ciphers == null) { return defaultToDefaultCiphers ? - defaultCiphers.toArray(new String[defaultCiphers.size()]) : - supportedCiphers.toArray(new String[supportedCiphers.size()]); + defaultCiphers.toArray(new String[0]) : + supportedCiphers.toArray(new String[0]); } else { List newCiphers = new ArrayList(supportedCiphers.size()); for (String c : ciphers) { @@ -56,7 +56,7 @@ public String[] filterCipherSuites(Iterable ciphers, List defaul } newCiphers.add(c); } - return newCiphers.toArray(new String[newCiphers.size()]); + return newCiphers.toArray(new String[0]); } } } diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java index 41fd9c6f6a8f..2b61391b4916 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java @@ -84,7 +84,7 @@ public class JdkSslContext extends SslContext { "TLSv1.2", "TLSv1.1", "TLSv1"); if (!protocols.isEmpty()) { - DEFAULT_PROTOCOLS = protocols.toArray(new String[protocols.size()]); + DEFAULT_PROTOCOLS = protocols.toArray(new String[0]); } else { DEFAULT_PROTOCOLS = engine.getEnabledProtocols(); } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index 4b031291e695..e614fccdcd34 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -438,7 +438,7 @@ private static void loadTcNative() throws Exception { libNames.add(staticLibName); NativeLibraryLoader.loadFirstAvailable(SSL.class.getClassLoader(), - libNames.toArray(new String[libNames.size()])); + libNames.toArray(new String[0])); } private static boolean initializeTcNative(String engine) throws Exception { diff --git a/handler/src/main/java/io/netty/handler/ssl/PemReader.java b/handler/src/main/java/io/netty/handler/ssl/PemReader.java index 016d215c2f83..4cddad9c912a 100644 --- a/handler/src/main/java/io/netty/handler/ssl/PemReader.java +++ b/handler/src/main/java/io/netty/handler/ssl/PemReader.java @@ -98,7 +98,7 @@ static ByteBuf[] readCertificates(InputStream in) throws CertificateException { throw new CertificateException("found no certificates in input stream"); } - return certs.toArray(new ByteBuf[certs.size()]); + return certs.toArray(new ByteBuf[0]); } static ByteBuf readPrivateKey(File file) throws KeyException { diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java index ea63bad6acde..749c540a3afd 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java @@ -246,7 +246,7 @@ public void requested( final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); try { final Set keyTypesSet = supportedClientKeyTypes(keyTypeBytes); - final String[] keyTypes = keyTypesSet.toArray(new String[keyTypesSet.size()]); + final String[] keyTypes = keyTypesSet.toArray(new String[0]); final X500Principal[] issuers; if (asn1DerEncodedPrincipals == null) { issuers = null; diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index 9a1a1c02454c..2a961ca024c1 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -265,7 +265,7 @@ public String run() { List nextProtoList = apn.protocols(); /* Set next protocols for next protocol negotiation extension, if specified */ if (!nextProtoList.isEmpty()) { - String[] appProtocols = nextProtoList.toArray(new String[nextProtoList.size()]); + String[] appProtocols = nextProtoList.toArray(new String[0]); int selectorBehavior = opensslSelectorFailureBehavior(apn.selectorFailureBehavior()); switch (apn.protocol()) { diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 94d45d109ba2..b98e9858d7be 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -1286,7 +1286,7 @@ public final synchronized boolean isOutboundDone() { @Override public final String[] getSupportedCipherSuites() { - return OpenSsl.AVAILABLE_CIPHER_SUITES.toArray(new String[OpenSsl.AVAILABLE_CIPHER_SUITES.size()]); + return OpenSsl.AVAILABLE_CIPHER_SUITES.toArray(new String[0]); } @Override @@ -1359,7 +1359,7 @@ public final void setEnabledCipherSuites(String[] cipherSuites) { @Override public final String[] getSupportedProtocols() { - return OpenSsl.SUPPORTED_PROTOCOLS_SET.toArray(new String[OpenSsl.SUPPORTED_PROTOCOLS_SET.size()]); + return OpenSsl.SUPPORTED_PROTOCOLS_SET.toArray(new String[0]); } @Override @@ -1373,7 +1373,7 @@ public final String[] getEnabledProtocols() { if (!isDestroyed()) { opts = SSL.getOptions(ssl); } else { - return enabled.toArray(new String[1]); + return enabled.toArray(new String[0]); } } if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1, PROTOCOL_TLS_V1)) { @@ -1391,7 +1391,7 @@ public final String[] getEnabledProtocols() { if (isProtocolEnabled(opts, SSL.SSL_OP_NO_SSLv3, PROTOCOL_SSL_V3)) { enabled.add(PROTOCOL_SSL_V3); } - return enabled.toArray(new String[enabled.size()]); + return enabled.toArray(new String[0]); } private static boolean isProtocolEnabled(int opts, int disableMask, String protocolString) { @@ -1955,7 +1955,7 @@ public String[] getValueNames() { if (values == null || values.isEmpty()) { return EmptyArrays.EMPTY_STRINGS; } - return values.keySet().toArray(new String[values.size()]); + return values.keySet().toArray(new String[0]); } private void notifyUnbound(Object value, String name) { diff --git a/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java b/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java index 132f1a0e5e52..265672309f8e 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java +++ b/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java @@ -53,7 +53,7 @@ public String[] filterCipherSuites(Iterable ciphers, List defaul newCiphers.add(c); } } - return newCiphers.toArray(new String[newCiphers.size()]); + return newCiphers.toArray(new String[0]); } } diff --git a/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java index b4fca297a797..454334501efd 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java @@ -172,7 +172,7 @@ public FingerprintTrustManagerFactory(byte[]... fingerprints) { list.add(f.clone()); } - this.fingerprints = list.toArray(new byte[list.size()][]); + this.fingerprints = list.toArray(new byte[0][]); } private static byte[][] toFingerprintArray(Iterable fingerprints) { @@ -197,7 +197,7 @@ private static byte[][] toFingerprintArray(Iterable fingerprints) { list.add(StringUtil.decodeHexDump(f)); } - return list.toArray(new byte[list.size()][]); + return list.toArray(new byte[0][]); } @Override diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index 55864910d8d0..9be57e546802 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -350,7 +350,7 @@ public void testSrcsLenOverFlowCorrectlyHandled() throws Exception { srcsLen += dup.capacity(); } - ByteBuffer[] srcs = srcList.toArray(new ByteBuffer[srcList.size()]); + ByteBuffer[] srcs = srcList.toArray(new ByteBuffer[0]); ByteBuffer dst = allocateBuffer( unwrapEngine(clientEngine).maxEncryptedPacketLength() - 1); diff --git a/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmarkBase.java b/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmarkBase.java index d3400d34635c..058658789f6f 100644 --- a/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmarkBase.java +++ b/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmarkBase.java @@ -93,8 +93,7 @@ protected static String[] removeAssertions(String[] jvmArgs) { } } if (jvmArgs.length != customArgs.size()) { - jvmArgs = new String[customArgs.size()]; - customArgs.toArray(jvmArgs); + jvmArgs = customArgs.toArray(new String[0]); } return jvmArgs; } diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java index 610a51b7daa4..ea98afaa8f15 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java @@ -142,7 +142,7 @@ public final class DefaultDnsServerAddressStreamProvider implements DnsServerAdd } DEFAULT_NAME_SERVER_LIST = Collections.unmodifiableList(defaultNameServers); - DEFAULT_NAME_SERVER_ARRAY = defaultNameServers.toArray(new InetSocketAddress[defaultNameServers.size()]); + DEFAULT_NAME_SERVER_ARRAY = defaultNameServers.toArray(new InetSocketAddress[0]); DEFAULT_NAME_SERVERS = sequential(DEFAULT_NAME_SERVER_ARRAY); } diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java index 5bd7baad2594..5619e176efcd 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java @@ -131,7 +131,7 @@ public class DnsNameResolver extends InetNameResolver { @SuppressWarnings("unchecked") List list = (List) nameservers.invoke(instance); - searchDomains = list.toArray(new String[list.size()]); + searchDomains = list.toArray(new String[0]); } catch (Exception ignore) { // Failed to get the system name search domain list. searchDomains = EmptyArrays.EMPTY_STRINGS; diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java index a5df3409dfef..78e700491d91 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java @@ -335,7 +335,7 @@ public DnsNameResolverBuilder searchDomains(Iterable searchDomains) { list.add(f); } - this.searchDomains = list.toArray(new String[list.size()]); + this.searchDomains = list.toArray(new String[0]); return this; } diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddresses.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddresses.java index ced160f5f8be..9dee31ab7f52 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddresses.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddresses.java @@ -187,7 +187,7 @@ private static InetSocketAddress[] sanitize(Iterable future) throws Exception { public void close() { final AddressResolver[] rArray; synchronized (resolvers) { - rArray = (AddressResolver[]) resolvers.values().toArray(new AddressResolver[resolvers.size()]); + rArray = (AddressResolver[]) resolvers.values().toArray(new AddressResolver[0]); resolvers.clear(); } diff --git a/transport-udt/src/test/java/io/netty/test/udt/util/CaliperMeasure.java b/transport-udt/src/test/java/io/netty/test/udt/util/CaliperMeasure.java index eb1a489f16a8..68aa2a4d5028 100644 --- a/transport-udt/src/test/java/io/netty/test/udt/util/CaliperMeasure.java +++ b/transport-udt/src/test/java/io/netty/test/udt/util/CaliperMeasure.java @@ -172,7 +172,7 @@ public Map variables() { } private static MeasurementSet measurementSet(final Map map) { - final Measurement[] array = map.values().toArray(new Measurement[map.size()]); + final Measurement[] array = map.values().toArray(new Measurement[0]); return new MeasurementSet(array); } diff --git a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java index 2a4c04e74162..310e9fb9fe7d 100644 --- a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java +++ b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java @@ -160,10 +160,10 @@ void init(Channel channel) throws Exception { final Entry, Object>[] currentChildOptions; final Entry, Object>[] currentChildAttrs; synchronized (childOptions) { - currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size())); + currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0)); } synchronized (childAttrs) { - currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size())); + currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0)); } p.addLast(new ChannelInitializer() { From 253522fc9157b9fb12f4f2d5b4f5160ffe6dd2b1 Mon Sep 17 00:00:00 2001 From: Bennett Lynch Date: Thu, 28 Jun 2018 23:25:23 -0700 Subject: [PATCH 077/417] Create SimpleUserEventChannelHandler convenience class (#7991) Motivation: Currently, the vast majority of userEventTriggered() implementations require the user to supply the boilerplate behavior of performing an instanceof check, handling if appropriate, and calling fireUserEventTriggered() otherwise. We can simplify this very common use case by creating a class that only matches user events of a given type, similar to the existing SimpleChannelInboundHandler class. Modifications: Create a new SimpleUserEventChannelHandler class Create accompanying SimpleUserEventChannelHandlerTest class Result: Users will be able to handle most events in a less verbose manner. --- .../SimpleUserEventChannelHandler.java | 120 ++++++++++++++++++ .../SimpleUserEventChannelHandlerTest.java | 101 +++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 transport/src/main/java/io/netty/channel/SimpleUserEventChannelHandler.java create mode 100644 transport/src/test/java/io/netty/channel/SimpleUserEventChannelHandlerTest.java diff --git a/transport/src/main/java/io/netty/channel/SimpleUserEventChannelHandler.java b/transport/src/main/java/io/netty/channel/SimpleUserEventChannelHandler.java new file mode 100644 index 000000000000..c976c2d26c25 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/SimpleUserEventChannelHandler.java @@ -0,0 +1,120 @@ +/* + * Copyright 2018 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: + * + * http://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.channel; + +import io.netty.util.ReferenceCountUtil; +import io.netty.util.internal.TypeParameterMatcher; + +/** + * {@link ChannelInboundHandlerAdapter} which allows to conveniently only handle a specific type of user events. + * + * For example, here is an implementation which only handle {@link String} user events. + * + *
+ *     public class StringEventHandler extends
+ *             {@link SimpleUserEventChannelHandler}<{@link String}> {
+ *
+ *         {@code @Override}
+ *         protected void eventReceived({@link ChannelHandlerContext} ctx, {@link String} evt)
+ *                 throws {@link Exception} {
+ *             System.out.println(evt);
+ *         }
+ *     }
+ * 
+ * + * Be aware that depending of the constructor parameters it will release all handled events by passing them to + * {@link ReferenceCountUtil#release(Object)}. In this case you may need to use + * {@link ReferenceCountUtil#retain(Object)} if you pass the object to the next handler in the {@link ChannelPipeline}. + */ +public abstract class SimpleUserEventChannelHandler extends ChannelInboundHandlerAdapter { + + private final TypeParameterMatcher matcher; + private final boolean autoRelease; + + /** + * see {@link #SimpleUserEventChannelHandler(boolean)} with {@code true} as boolean parameter. + */ + protected SimpleUserEventChannelHandler() { + this(true); + } + + /** + * Create a new instance which will try to detect the types to match out of the type parameter of the class. + * + * @param autoRelease {@code true} if handled events should be released automatically by passing them to + * {@link ReferenceCountUtil#release(Object)}. + */ + protected SimpleUserEventChannelHandler(boolean autoRelease) { + matcher = TypeParameterMatcher.find(this, SimpleUserEventChannelHandler.class, "I"); + this.autoRelease = autoRelease; + } + + /** + * see {@link #SimpleUserEventChannelHandler(Class, boolean)} with {@code true} as boolean value. + */ + protected SimpleUserEventChannelHandler(Class eventType) { + this(eventType, true); + } + + /** + * Create a new instance + * + * @param eventType The type of events to match + * @param autoRelease {@code true} if handled events should be released automatically by passing them to + * {@link ReferenceCountUtil#release(Object)}. + */ + protected SimpleUserEventChannelHandler(Class eventType, boolean autoRelease) { + matcher = TypeParameterMatcher.get(eventType); + this.autoRelease = autoRelease; + } + + /** + * Returns {@code true} if the given user event should be handled. If {@code false} it will be passed to the next + * {@link ChannelInboundHandler} in the {@link ChannelPipeline}. + */ + protected boolean acceptEvent(Object evt) throws Exception { + return matcher.match(evt); + } + + @Override + public final void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + boolean release = true; + try { + if (acceptEvent(evt)) { + @SuppressWarnings("unchecked") + I ievt = (I) evt; + eventReceived(ctx, ievt); + } else { + release = false; + ctx.fireUserEventTriggered(evt); + } + } finally { + if (autoRelease && release) { + ReferenceCountUtil.release(evt); + } + } + } + + /** + * Is called for each user event triggered of type {@link I}. + * + * @param ctx the {@link ChannelHandlerContext} which this {@link SimpleUserEventChannelHandler} belongs to + * @param evt the user event to handle + * + * @throws Exception is thrown if an error occurred + */ + protected abstract void eventReceived(ChannelHandlerContext ctx, I evt) throws Exception; +} diff --git a/transport/src/test/java/io/netty/channel/SimpleUserEventChannelHandlerTest.java b/transport/src/test/java/io/netty/channel/SimpleUserEventChannelHandlerTest.java new file mode 100644 index 000000000000..c0801c0be39f --- /dev/null +++ b/transport/src/test/java/io/netty/channel/SimpleUserEventChannelHandlerTest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2018 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: + * + * http://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.channel; + +import io.netty.buffer.DefaultByteBufHolder; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +public class SimpleUserEventChannelHandlerTest { + + private FooEventCatcher fooEventCatcher; + private AllEventCatcher allEventCatcher; + private EmbeddedChannel channel; + + @Before + public void setUp() { + fooEventCatcher = new FooEventCatcher(); + allEventCatcher = new AllEventCatcher(); + channel = new EmbeddedChannel(fooEventCatcher, allEventCatcher); + } + + @Test + public void testTypeMatch() { + FooEvent fooEvent = new FooEvent(); + channel.pipeline().fireUserEventTriggered(fooEvent); + assertEquals(1, fooEventCatcher.caughtEvents.size()); + assertEquals(0, allEventCatcher.caughtEvents.size()); + assertEquals(0, fooEvent.refCnt()); + assertFalse(channel.finish()); + } + + @Test + public void testTypeMismatch() { + BarEvent barEvent = new BarEvent(); + channel.pipeline().fireUserEventTriggered(barEvent); + assertEquals(0, fooEventCatcher.caughtEvents.size()); + assertEquals(1, allEventCatcher.caughtEvents.size()); + assertTrue(barEvent.release()); + assertFalse(channel.finish()); + } + + static final class FooEvent extends DefaultByteBufHolder { + FooEvent() { + super(Unpooled.buffer()); + } + } + + static final class BarEvent extends DefaultByteBufHolder { + BarEvent() { + super(Unpooled.buffer()); + } + } + + static final class FooEventCatcher extends SimpleUserEventChannelHandler { + + public List caughtEvents; + + FooEventCatcher() { + caughtEvents = new ArrayList(); + } + + @Override + protected void eventReceived(ChannelHandlerContext ctx, FooEvent evt) { + caughtEvents.add(evt); + } + } + + static final class AllEventCatcher extends ChannelInboundHandlerAdapter { + + public List caughtEvents; + + AllEventCatcher() { + caughtEvents = new ArrayList(); + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { + caughtEvents.add(evt); + } + } +} From 7f5e77484caa05504b4e6c4527ac7bf1732d01c8 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 3 Jul 2018 07:05:12 +0200 Subject: [PATCH 078/417] Fix support for shading native libraries which was broken in b818852cdb0ee56ce3cf939e2faa538d519baabd. (#8091) Motivation: b818852cdb0ee56ce3cf939e2faa538d519baabd broke support for shading the native libraries in netty as it missed to respect the package prefix that is used when shading. Modifications: Correctly respect package prefix for constructor argument and include the used classname when logging that we could not find the constructor. Result: Be able to shade native libraries of netty again. --- .../src/main/c/netty_unix_socket.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/transport-native-unix-common/src/main/c/netty_unix_socket.c b/transport-native-unix-common/src/main/c/netty_unix_socket.c index 6bf36feb8a6c..380fcda24ae4 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_socket.c +++ b/transport-native-unix-common/src/main/c/netty_unix_socket.c @@ -1010,21 +1010,32 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { dynamicMethods = NULL; char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress"); jclass localDatagramSocketAddressClass = (*env)->FindClass(env, nettyClassName); - free(nettyClassName); - nettyClassName = NULL; if (localDatagramSocketAddressClass == NULL) { + free(nettyClassName); + nettyClassName = NULL; // pending exception... return JNI_ERR; } datagramSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localDatagramSocketAddressClass); if (datagramSocketAddressClass == NULL) { + free(nettyClassName); + nettyClassName = NULL; // out-of-memory! netty_unix_errors_throwOutOfMemoryError(env); return JNI_ERR; } - datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "", "(Ljava/lang/String;IILio/netty/channel/unix/DatagramSocketAddress;)V"); + + // Respect shading... + char parameters[1024] = {0}; + snprintf(parameters, sizeof(parameters), "(Ljava/lang/String;IIL%s;)V", nettyClassName); + free(nettyClassName); + nettyClassName = NULL; + + datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "", parameters); if (datagramSocketAddrMethodId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get method ID: DatagramSocketAddress.(String, int, int, DatagramSocketAddress)"); + char msg[1024] = {0}; + snprintf(msg, sizeof(msg), "failed to get method ID: %s.(String, int, int, %s)", nettyClassName, nettyClassName); + netty_unix_errors_throwRuntimeException(env, msg); return JNI_ERR; } jclass localInetSocketAddressClass = (*env)->FindClass(env, "java/net/InetSocketAddress"); From 804d8434dc7026d8ae9df1a3a2431fd3034c2594 Mon Sep 17 00:00:00 2001 From: Scott Mitchell Date: Tue, 3 Jul 2018 19:51:16 -0700 Subject: [PATCH 079/417] HTTP/2 goaway connection state update sequencing (#8080) Motivation: The Http2Connection state is updated by the DefaultHttp2ConnectionDecoder after the frame listener is notified of the goaway frame. If the listener sends a frame synchronously this means the connection state will not know about the goaway it just received and we may send frames that are not allowed on the connection. This may also mean a stream object is created but it may never get taken out of the stream map unless some other event occurs (e.g. timeout). Modifications: - The Http2Connection state should be updated before the listener is notified of the goaway - The Http2Connection state modification and validation should be self contained when processing a goaway instead of partially in the decoder. Result: No more creating streams and sending frames after a goaway has been sent or received. --- .../codec/http2/DefaultHttp2Connection.java | 69 ++++++++-------- .../http2/DefaultHttp2ConnectionDecoder.java | 12 +-- .../handler/codec/http2/Http2Connection.java | 21 ++++- .../codec/http2/Http2ConnectionHandler.java | 62 +++++++-------- .../DefaultHttp2ConnectionDecoderTest.java | 7 -- .../DefaultHttp2ConnectionEncoderTest.java | 6 +- .../http2/DefaultHttp2ConnectionTest.java | 22 +++++- .../http2/Http2ConnectionHandlerTest.java | 7 ++ .../http2/Http2ConnectionRoundtripTest.java | 78 +++++++++++++++++++ .../http2/StreamBufferingEncoderTest.java | 4 +- 10 files changed, 199 insertions(+), 89 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java index 8dd0a31cd28f..8590e06fa241 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java @@ -25,7 +25,6 @@ import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.UnaryPromiseNotifier; import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.UnstableApi; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -225,7 +224,12 @@ public boolean goAwayReceived() { } @Override - public void goAwayReceived(final int lastKnownStream, long errorCode, ByteBuf debugData) { + public void goAwayReceived(final int lastKnownStream, long errorCode, ByteBuf debugData) throws Http2Exception { + if (localEndpoint.lastStreamKnownByPeer() >= 0 && localEndpoint.lastStreamKnownByPeer() < lastKnownStream) { + throw connectionError(PROTOCOL_ERROR, "lastStreamId MUST NOT increase. Current value: %d new value: %d", + localEndpoint.lastStreamKnownByPeer(), lastKnownStream); + } + localEndpoint.lastStreamKnownByPeer(lastKnownStream); for (int i = 0; i < listeners.size(); ++i) { try { @@ -235,19 +239,7 @@ public void goAwayReceived(final int lastKnownStream, long errorCode, ByteBuf de } } - try { - forEachActiveStream(new Http2StreamVisitor() { - @Override - public boolean visit(Http2Stream stream) { - if (stream.id() > lastKnownStream && localEndpoint.isValidStreamId(stream.id())) { - stream.close(); - } - return true; - } - }); - } catch (Http2Exception e) { - PlatformDependent.throwException(e); - } + closeStreamsGreaterThanLastKnownStreamId(lastKnownStream, localEndpoint); } @Override @@ -256,7 +248,20 @@ public boolean goAwaySent() { } @Override - public void goAwaySent(final int lastKnownStream, long errorCode, ByteBuf debugData) { + public boolean goAwaySent(final int lastKnownStream, long errorCode, ByteBuf debugData) throws Http2Exception { + if (remoteEndpoint.lastStreamKnownByPeer() >= 0) { + // Protect against re-entrancy. Could happen if writing the frame fails, and error handling + // treating this is a connection handler and doing a graceful shutdown... + if (lastKnownStream == remoteEndpoint.lastStreamKnownByPeer()) { + return false; + } + if (lastKnownStream > remoteEndpoint.lastStreamKnownByPeer()) { + throw connectionError(PROTOCOL_ERROR, "Last stream identifier must not increase between " + + "sending multiple GOAWAY frames (was '%d', is '%d').", + remoteEndpoint.lastStreamKnownByPeer(), lastKnownStream); + } + } + remoteEndpoint.lastStreamKnownByPeer(lastKnownStream); for (int i = 0; i < listeners.size(); ++i) { try { @@ -266,19 +271,21 @@ public void goAwaySent(final int lastKnownStream, long errorCode, ByteBuf debugD } } - try { - forEachActiveStream(new Http2StreamVisitor() { - @Override - public boolean visit(Http2Stream stream) { - if (stream.id() > lastKnownStream && remoteEndpoint.isValidStreamId(stream.id())) { - stream.close(); - } - return true; + closeStreamsGreaterThanLastKnownStreamId(lastKnownStream, remoteEndpoint); + return true; + } + + private void closeStreamsGreaterThanLastKnownStreamId(final int lastKnownStream, + final DefaultEndpoint endpoint) throws Http2Exception { + forEachActiveStream(new Http2StreamVisitor() { + @Override + public boolean visit(Http2Stream stream) { + if (stream.id() > lastKnownStream && endpoint.isValidStreamId(stream.id())) { + stream.close(); } - }); - } catch (Http2Exception e) { - PlatformDependent.throwException(e); - } + return true; + } + }); } /** @@ -863,10 +870,10 @@ private void updateMaxStreams() { private void checkNewStreamAllowed(int streamId, State state) throws Http2Exception { assert state != IDLE; - if (goAwayReceived() && streamId > localEndpoint.lastStreamKnownByPeer()) { + if (lastStreamKnownByPeer >= 0 && streamId > lastStreamKnownByPeer) { throw streamError(streamId, REFUSED_STREAM, - "Cannot create stream %d since this endpoint has received a GOAWAY frame with last stream id %d.", - streamId, localEndpoint.lastStreamKnownByPeer()); + "Cannot create stream %d greater than Last-Stream-ID %d from GOAWAY.", + streamId, lastStreamKnownByPeer); } if (!isValidStreamId(streamId)) { if (streamId < 0) { diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java index 49335e95e737..9114f05ff06f 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java @@ -158,12 +158,8 @@ private int unconsumedBytes(Http2Stream stream) { void onGoAwayRead0(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) throws Http2Exception { - if (connection.goAwayReceived() && connection.local().lastStreamKnownByPeer() < lastStreamId) { - throw connectionError(PROTOCOL_ERROR, "lastStreamId MUST NOT increase. Current value: %d new value: %d", - connection.local().lastStreamKnownByPeer(), lastStreamId); - } - listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData); connection.goAwayReceived(lastStreamId, errorCode, debugData); + listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData); } void onUnknownFrame0(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, @@ -535,12 +531,18 @@ private boolean shouldIgnoreHeadersOrDataFrame(ChannelHandlerContext ctx, int st throw streamError(streamId, STREAM_CLOSED, "Received %s frame for an unknown stream %d", frameName, streamId); } else if (stream.isResetSent() || streamCreatedAfterGoAwaySent(streamId)) { + // If we have sent a reset stream it is assumed the stream will be closed after the write completes. + // If we have not sent a reset, but the stream was created after a GoAway this is not supported by + // DefaultHttp2Connection and if a custom Http2Connection is used it is assumed the lifetime is managed + // elsewhere so we don't close the stream or otherwise modify the stream's state. + if (logger.isInfoEnabled()) { logger.info("{} ignoring {} frame for stream {} {}", ctx.channel(), frameName, stream.isResetSent() ? "RST_STREAM sent." : ("Stream created after GOAWAY sent. Last known stream by peer " + connection.remote().lastStreamKnownByPeer())); } + return true; } return false; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Connection.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Connection.java index f43820bf2392..7639e0840634 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Connection.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Connection.java @@ -326,8 +326,15 @@ interface PropertyKey { /** * Indicates that a {@code GOAWAY} was received from the remote endpoint and sets the last known stream. + * @param lastKnownStream The Last-Stream-ID in the + * GOAWAY frame. + * @param errorCode the Error Code in the + * GOAWAY frame. + * @param message The Additional Debug Data in the + * GOAWAY frame. Note that reference count ownership + * belongs to the caller (ownership is not transferred to this method). */ - void goAwayReceived(int lastKnownStream, long errorCode, ByteBuf message); + void goAwayReceived(int lastKnownStream, long errorCode, ByteBuf message) throws Http2Exception; /** * Indicates whether or not a {@code GOAWAY} was sent to the remote endpoint. @@ -335,7 +342,15 @@ interface PropertyKey { boolean goAwaySent(); /** - * Indicates that a {@code GOAWAY} was sent to the remote endpoint and sets the last known stream. + * Updates the local state of this {@link Http2Connection} as a result of a {@code GOAWAY} to send to the remote + * endpoint. + * @param lastKnownStream The Last-Stream-ID in the + * GOAWAY frame. + * @param errorCode the Error Code in the + * GOAWAY frame. + * GOAWAY frame. Note that reference count ownership + * belongs to the caller (ownership is not transferred to this method). + * @return {@code true} if the corresponding {@code GOAWAY} frame should be sent to the remote endpoint. */ - void goAwaySent(int lastKnownStream, long errorCode, ByteBuf message); + boolean goAwaySent(int lastKnownStream, long errorCode, ByteBuf message) throws Http2Exception; } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java index e44149de5bd6..3151f1ccb2f1 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java @@ -794,47 +794,37 @@ public void operationComplete(ChannelFuture future) throws Exception { @Override public ChannelFuture goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode, final ByteBuf debugData, ChannelPromise promise) { + promise = promise.unvoid(); + final Http2Connection connection = connection(); try { - promise = promise.unvoid(); - final Http2Connection connection = connection(); - if (connection().goAwaySent()) { - // Protect against re-entrancy. Could happen if writing the frame fails, and error handling - // treating this is a connection handler and doing a graceful shutdown... - if (lastStreamId == connection().remote().lastStreamKnownByPeer()) { - // Release the data and notify the promise - debugData.release(); - return promise.setSuccess(); - } - if (lastStreamId > connection.remote().lastStreamKnownByPeer()) { - throw connectionError(PROTOCOL_ERROR, "Last stream identifier must not increase between " + - "sending multiple GOAWAY frames (was '%d', is '%d').", - connection.remote().lastStreamKnownByPeer(), lastStreamId); - } + if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) { + debugData.release(); + promise.trySuccess(); + return promise; } + } catch (Throwable cause) { + debugData.release(); + promise.tryFailure(cause); + return promise; + } - connection.goAwaySent(lastStreamId, errorCode, debugData); - - // Need to retain before we write the buffer because if we do it after the refCnt could already be 0 and - // result in an IllegalRefCountException. - debugData.retain(); - ChannelFuture future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise); - - if (future.isDone()) { - processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future); - } else { - future.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future); - } - }); - } + // Need to retain before we write the buffer because if we do it after the refCnt could already be 0 and + // result in an IllegalRefCountException. + debugData.retain(); + ChannelFuture future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise); - return future; - } catch (Throwable cause) { // Make sure to catch Throwable because we are doing a retain() in this method. - debugData.release(); - return promise.setFailure(cause); + if (future.isDone()) { + processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future); + } else { + future.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future); + } + }); } + + return future; } /** diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java index 8d71ddeaffe4..7e87d52893c2 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java @@ -674,13 +674,6 @@ public void rstStreamReadAfterGoAwayShouldSucceed() throws Exception { verify(listener).onRstStreamRead(eq(ctx), anyInt(), anyLong()); } - @Test(expected = Http2Exception.class) - public void goawayIncreasedLastStreamIdShouldThrow() throws Exception { - when(local.lastStreamKnownByPeer()).thenReturn(1); - when(connection.goAwayReceived()).thenReturn(true); - decode().onGoAwayRead(ctx, 3, 2L, EMPTY_BUFFER); - } - @Test(expected = Http2Exception.class) public void rstStreamReadForUnknownStreamShouldThrow() throws Exception { when(connection.streamMayHaveExisted(STREAM_ID)).thenReturn(false); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java index 5f9a6b18719c..cdbfaa4ebdb4 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java @@ -770,7 +770,7 @@ public void canWriteDataFrameAfterGoAwayReceived() throws Exception { } @Test - public void canWriteHeaderFrameAfterGoAwayReceived() { + public void canWriteHeaderFrameAfterGoAwayReceived() throws Http2Exception { writeAllFlowControlledFrames(); goAwayReceived(STREAM_ID); ChannelPromise promise = newPromise(); @@ -803,11 +803,11 @@ private Http2Stream stream(int streamId) { return connection.stream(streamId); } - private void goAwayReceived(int lastStreamId) { + private void goAwayReceived(int lastStreamId) throws Http2Exception { connection.goAwayReceived(lastStreamId, 0, EMPTY_BUFFER); } - private void goAwaySent(int lastStreamId) { + private void goAwaySent(int lastStreamId) throws Http2Exception { connection.goAwaySent(lastStreamId, 0, EMPTY_BUFFER); } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java index ef385beed646..69183e0843d8 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java @@ -47,8 +47,8 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -423,11 +423,29 @@ public void reserveWithPushDisallowedShouldThrow() throws Http2Exception { } @Test(expected = Http2Exception.class) - public void goAwayReceivedShouldDisallowCreation() throws Http2Exception { + public void goAwayReceivedShouldDisallowLocalCreation() throws Http2Exception { + server.goAwayReceived(0, 1L, Unpooled.EMPTY_BUFFER); + server.local().createStream(3, true); + } + + @Test + public void goAwayReceivedShouldAllowRemoteCreation() throws Http2Exception { server.goAwayReceived(0, 1L, Unpooled.EMPTY_BUFFER); server.remote().createStream(3, true); } + @Test(expected = Http2Exception.class) + public void goAwaySentShouldDisallowRemoteCreation() throws Http2Exception { + server.goAwaySent(0, 1L, Unpooled.EMPTY_BUFFER); + server.remote().createStream(2, true); + } + + @Test + public void goAwaySentShouldAllowLocalCreation() throws Http2Exception { + server.goAwaySent(0, 1L, Unpooled.EMPTY_BUFFER); + server.local().createStream(2, true); + } + @Test public void closeShouldSucceed() throws Http2Exception { Http2Stream stream = server.remote().createStream(3, true); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java index be5c41b0e55d..223d6005f495 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java @@ -189,6 +189,7 @@ public Http2Stream answer(InvocationOnMock in) throws Throwable { when(connection.stream(NON_EXISTANT_STREAM_ID)).thenReturn(null); when(connection.numActiveStreams()).thenReturn(1); when(connection.stream(STREAM_ID)).thenReturn(stream); + when(connection.goAwaySent(anyInt(), anyLong(), any(ByteBuf.class))).thenReturn(true); when(stream.open(anyBoolean())).thenReturn(stream); when(encoder.writeSettings(eq(ctx), any(Http2Settings.class), eq(promise))).thenReturn(future); when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); @@ -638,6 +639,12 @@ public void cannotSendGoAwayFrameWithIncreasingLastStreamIds() throws Exception when(connection.goAwaySent()).thenReturn(true); when(remote.lastStreamKnownByPeer()).thenReturn(STREAM_ID); + doAnswer(new Answer() { + @Override + public Boolean answer(InvocationOnMock invocationOnMock) { + throw new IllegalStateException(); + } + }).when(connection).goAwaySent(anyInt(), anyLong(), any(ByteBuf.class)); handler.goAway(ctx, STREAM_ID + 2, errorCode, data, promise); assertTrue(promise.isDone()); assertFalse(promise.isSuccess()); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java index a909b9eb1c9f..f4a4b6c94bcd 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java @@ -889,6 +889,84 @@ public void operationComplete(ChannelFuture future) throws Exception { setServerGracefulShutdownTime(0); } + @Test + public void createStreamSynchronouslyAfterGoAwayReceivedShouldFailLocally() throws Exception { + bootstrapEnv(1, 1, 2, 1, 1); + + final CountDownLatch clientGoAwayLatch = new CountDownLatch(1); + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocationOnMock) throws Throwable { + clientGoAwayLatch.countDown(); + return null; + } + }).when(clientListener).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class)); + + // We want both sides to do graceful shutdown during the test. + setClientGracefulShutdownTime(10000); + setServerGracefulShutdownTime(10000); + + final Http2Headers headers = dummyHeaders(); + final AtomicReference clientWriteAfterGoAwayFutureRef = new AtomicReference(); + final CountDownLatch clientWriteAfterGoAwayLatch = new CountDownLatch(1); + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocationOnMock) throws Throwable { + ChannelFuture f = http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0, + true, newPromise()); + clientWriteAfterGoAwayFutureRef.set(f); + f.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + clientWriteAfterGoAwayLatch.countDown(); + } + }); + http2Client.flush(ctx()); + return null; + } + }).when(clientListener).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class)); + + runInChannel(clientChannel, new Http2Runnable() { + @Override + public void run() throws Http2Exception { + http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, + true, newPromise()); + http2Client.flush(ctx()); + } + }); + + assertTrue(serverSettingsAckLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); + + // Server has received the headers, so the stream is open + assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); + + runInChannel(serverChannel, new Http2Runnable() { + @Override + public void run() throws Http2Exception { + http2Server.encoder().writeGoAway(serverCtx(), 3, NO_ERROR.code(), EMPTY_BUFFER, serverNewPromise()); + http2Server.flush(serverCtx()); + } + }); + + // Wait for the client's write operation to complete. + assertTrue(clientWriteAfterGoAwayLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); + + ChannelFuture clientWriteAfterGoAwayFuture = clientWriteAfterGoAwayFutureRef.get(); + assertNotNull(clientWriteAfterGoAwayFuture); + Throwable clientCause = clientWriteAfterGoAwayFuture.cause(); + assertThat(clientCause, is(instanceOf(Http2Exception.StreamException.class))); + assertEquals(Http2Error.REFUSED_STREAM.code(), ((Http2Exception.StreamException) clientCause).error().code()); + + // Wait for the server to receive a GO_AWAY, but this is expected to timeout! + assertFalse(goAwayLatch.await(1, SECONDS)); + verify(serverListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), + any(ByteBuf.class)); + + // Shutdown shouldn't wait for the server to close streams + setClientGracefulShutdownTime(0); + setServerGracefulShutdownTime(0); + } + @Test public void flowControlProperlyChunksLargeMessage() throws Exception { final Http2Headers headers = dummyHeaders(); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/StreamBufferingEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/StreamBufferingEncoderTest.java index a1768cb85769..15dc168b4da8 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/StreamBufferingEncoderTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/StreamBufferingEncoderTest.java @@ -222,7 +222,7 @@ public void alternatingWritesToActiveAndBufferedStreams() { } @Test - public void bufferingNewStreamFailsAfterGoAwayReceived() { + public void bufferingNewStreamFailsAfterGoAwayReceived() throws Http2Exception { encoder.writeSettingsAck(ctx, newPromise()); setMaxConcurrentStreams(0); connection.goAwayReceived(1, 8, EMPTY_BUFFER); @@ -235,7 +235,7 @@ public void bufferingNewStreamFailsAfterGoAwayReceived() { } @Test - public void receivingGoAwayFailsBufferedStreams() { + public void receivingGoAwayFailsBufferedStreams() throws Http2Exception { encoder.writeSettingsAck(ctx, newPromise()); setMaxConcurrentStreams(5); From fa8f967852c04fd99ae15ee2bd1047595a97417e Mon Sep 17 00:00:00 2001 From: Scott Mitchell Date: Tue, 3 Jul 2018 20:30:12 -0700 Subject: [PATCH 080/417] netty_unix_socket free nettyClassName after using it Motivation: netty_unix_socket attempts to use nettyClassName in an error message, but previously freed the memory. We should wait to free the memory until after we use it. Modifications: - Free nettyClassName after using it in snprintf Result: More useful error message. --- .../src/main/c/netty_unix_socket.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/transport-native-unix-common/src/main/c/netty_unix_socket.c b/transport-native-unix-common/src/main/c/netty_unix_socket.c index 380fcda24ae4..3dd0920b198f 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_socket.c +++ b/transport-native-unix-common/src/main/c/netty_unix_socket.c @@ -1028,16 +1028,20 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { // Respect shading... char parameters[1024] = {0}; snprintf(parameters, sizeof(parameters), "(Ljava/lang/String;IIL%s;)V", nettyClassName); - free(nettyClassName); - nettyClassName = NULL; datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "", parameters); if (datagramSocketAddrMethodId == NULL) { char msg[1024] = {0}; snprintf(msg, sizeof(msg), "failed to get method ID: %s.(String, int, int, %s)", nettyClassName, nettyClassName); + free(nettyClassName); + nettyClassName = NULL; netty_unix_errors_throwRuntimeException(env, msg); return JNI_ERR; } + + free(nettyClassName); + nettyClassName = NULL; + jclass localInetSocketAddressClass = (*env)->FindClass(env, "java/net/InetSocketAddress"); if (localInetSocketAddressClass == NULL) { // pending exception... From 7f95506132fc0556b50aa77d1baf9f054452378f Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Thu, 5 Jul 2018 18:09:23 -0600 Subject: [PATCH 081/417] Don't send a RST on close of the stream may not have existed (#8086) Motivation: When a Http2MultiplexCodec stream channel fails to write the first HEADERS it will forcibly close, and that will trigger sending a RST_STREAM, which is commonly a connection level protocol error. This is because it has what looks like a valid stream id, but didn't check with the connection as to whether the stream may have actually existed. Modifications: Instead of checking if the stream was just a valid looking id ( > 0) we check with the connection as to whether it may have existed at all. Result: We no longer send a RST_STREAM frame from Http2MultiplexCodec for idle streams. --- .../codec/http2/Http2MultiplexCodec.java | 7 +-- .../codec/http2/Http2MultiplexCodecTest.java | 48 ++++++++++++++++++- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java index 521b897a9ca5..9c3874c4d3a1 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java @@ -906,9 +906,10 @@ public void operationComplete(ChannelFuture future) throws Exception { final boolean wasActive = isActive(); - // Only ever send a reset frame if the connection is still alive as otherwise it makes no sense at - // all anyway. - if (parent().isActive() && !streamClosedWithoutError && isStreamIdValid(stream().id())) { + // Only ever send a reset frame if the connection is still alive and if the stream may have existed + // as otherwise we may send a RST on a stream in an invalid state and cause a connection error. + if (parent().isActive() && !streamClosedWithoutError && + connection().streamMayHaveExisted(stream().id())) { Http2StreamFrame resetFrame = new DefaultHttp2ResetFrame(Http2Error.CANCEL).stream(stream()); write(resetFrame, unsafe().voidPromise()); flush(); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java index 314bef7c23ee..f96b12bbe6b5 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java @@ -308,6 +308,43 @@ public void channelActive(ChannelHandlerContext ctx) throws Exception { assertEquals(Http2Error.CANCEL.code(), reset.errorCode()); } + @Test + public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() { + writer = new Writer() { + private boolean headersWritten; + @Override + void write(Object msg, ChannelPromise promise) { + // 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. + if (!headersWritten && msg instanceof Http2HeadersFrame) { + headersWritten = true; + Http2HeadersFrame headersFrame = (Http2HeadersFrame) msg; + final TestableHttp2MultiplexCodec.Stream stream = + (TestableHttp2MultiplexCodec.Stream) headersFrame.stream(); + stream.id = 1; + promise.setFailure(new Exception("boom")); + } else { + super.write(msg, promise); + } + } + }; + + childChannelInitializer.handler = new ChannelInboundHandlerAdapter() { + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); + ctx.fireChannelActive(); + } + }; + + Channel childChannel = newOutboundStream(); + assertFalse(childChannel.isActive()); + + childChannel.close(); + parentChannel.runPendingTasks(); + assertTrue(parentChannel.outboundMessages().isEmpty()); + } + @Test public void inboundRstStreamFireChannelInactive() { LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); @@ -745,7 +782,16 @@ private Http2FrameStream readOutboundHeadersAndAssignId() { assertNotNull(headersFrame); assertNotNull(headersFrame.stream()); assertFalse(Http2CodecUtil.isStreamIdValid(headersFrame.stream().id())); - ((TestableHttp2MultiplexCodec.Stream) headersFrame.stream()).id = outboundStream.id(); + TestableHttp2MultiplexCodec.Stream frameStream = (TestableHttp2MultiplexCodec.Stream) headersFrame.stream(); + frameStream.id = outboundStream.id(); + // Create the stream in the Http2Connection. + try { + Http2Stream stream = codec.connection().local().createStream( + headersFrame.stream().id(), headersFrame.isEndStream()); + frameStream.stream = stream; + } catch (Exception ex) { + throw new IllegalStateException("Failed to create a stream", ex); + } // Now read it and complete the write promise. assertSame(headersFrame, parentChannel.readOutbound()); From fef462c04333950201ddbfd505c0af730a0c2ffd Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Sat, 7 Jul 2018 11:45:27 -0700 Subject: [PATCH 082/417] Deprecate Unpooled.unmodifiableBuffer(ByteBuf...) (#8096) Motivation: Recent PR https://github.com/netty/netty/pull/8040 introduced Unpooled.wrappedUnmodifiableBuffer(ByteBuf...) which has the same behaviour but wraps the provided array directly. This is preferred for most uses (including varargs-based use) and if there are any unusual cases of an explicit array which is re-used before the ByteBuf is finished with, it can just be copied first. Modifications: Added @Deprecated annotation and javadoc to Unpooled.unmodifiableBuffer(ByteBuf...). Result: Unpooled.unmodifiableBuffer(ByteBuf...) will be deprecated. --- buffer/src/main/java/io/netty/buffer/Unpooled.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/netty/buffer/Unpooled.java index a48ad0952589..f6bec336dda7 100644 --- a/buffer/src/main/java/io/netty/buffer/Unpooled.java +++ b/buffer/src/main/java/io/netty/buffer/Unpooled.java @@ -882,7 +882,10 @@ public static ByteBuf unreleasableBuffer(ByteBuf buf) { /** * Wrap the given {@link ByteBuf}s in an unmodifiable {@link ByteBuf}. Be aware the returned {@link ByteBuf} will * not try to slice the given {@link ByteBuf}s to reduce GC-Pressure. + * + * @deprecated Use {@link #wrappedUnmodifiableBuffer(ByteBuf...)}. */ + @Deprecated public static ByteBuf unmodifiableBuffer(ByteBuf... buffers) { return wrappedUnmodifiableBuffer(true, buffers); } @@ -891,7 +894,7 @@ public static ByteBuf unmodifiableBuffer(ByteBuf... buffers) { * Wrap the given {@link ByteBuf}s in an unmodifiable {@link ByteBuf}. Be aware the returned {@link ByteBuf} will * not try to slice the given {@link ByteBuf}s to reduce GC-Pressure. * - * The returned {@link ByteBuf} wraps the provided array directly, and so should not be subsequently modified. + * The returned {@link ByteBuf} may wrap the provided array directly, and so should not be subsequently modified. */ public static ByteBuf wrappedUnmodifiableBuffer(ByteBuf... buffers) { return wrappedUnmodifiableBuffer(false, buffers); From cda4f88ca247d3a028deed52e6024cbf5a880b12 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 9 Jul 2018 03:50:26 -0400 Subject: [PATCH 083/417] Correctly release inbound data in example. (#8105) Motivation: We need to release the inbound data to ensure there are no leaks. Modifications: Extend SimpleChannelInboundHandler which will release inbound data by default. Result: No more leaks. --- .../http/helloworld/HttpHelloWorldServerHandler.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java b/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java index af142dc11d7c..3faf51af415b 100644 --- a/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java +++ b/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java @@ -18,16 +18,18 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpRequest; import io.netty.util.AsciiString; + import static io.netty.handler.codec.http.HttpResponseStatus.*; import static io.netty.handler.codec.http.HttpVersion.*; -public class HttpHelloWorldServerHandler extends ChannelInboundHandlerAdapter { +public class HttpHelloWorldServerHandler extends SimpleChannelInboundHandler { private static final byte[] CONTENT = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' }; private static final AsciiString CONTENT_TYPE = AsciiString.cached("Content-Type"); @@ -41,7 +43,7 @@ public void channelReadComplete(ChannelHandlerContext ctx) { } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { + public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) { if (msg instanceof HttpRequest) { HttpRequest req = (HttpRequest) msg; From 092073893201d515eb2ee375849efae7cc9065db Mon Sep 17 00:00:00 2001 From: Sebastian Utz Date: Mon, 9 Jul 2018 21:57:35 +0200 Subject: [PATCH 084/417] Do not log explicit no unsafe, fixes helper method. (#8111) Motivation: Users should not see a scary log message when Netty is initialized if Netty configuration explicitly disables unsafe. The log message that produces this warning was previously guarded but by recent refactoring a bug was introduced inside the guard helper method. Modifications: This commit brings back the guard against the scary log message if unsafe is explicitly disabled. Result: No log message is produced when unsafe is unavailable because Netty was told to not look for it. Relates https://github.com/netty/netty/pull/5624, https://github.com/netty/netty/pull/6696 --- .../main/java/io/netty/util/internal/PlatformDependent0.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java index 7d56356cc4bb..c6ebb55131d7 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java @@ -368,7 +368,7 @@ public Object run() { } static boolean isExplicitNoUnsafe() { - return EXPLICIT_NO_UNSAFE_CAUSE == null; + return EXPLICIT_NO_UNSAFE_CAUSE != null; } private static Throwable explicitNoUnsafeCause0() { From 6afab517b04e4aaf0c70e68e3b2888c06129197c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 9 Jul 2018 15:58:12 -0400 Subject: [PATCH 085/417] Guard against calling PoolThreadCache.free() multiple times. (#8108) Motivation: 5b1fe611a637c362a60b391079fff73b1a4ef912 introduced the usage of a finalizer as last resort for PoolThreadCache. As we may call free() from the FastThreadLocal.onRemoval(...) and finalize() we need to guard against multiple calls as otherwise we will corrupt internal state (that is used for metrics). Modifications: Use AtomicBoolean to guard against multiple calls of PoolThreadCache.free(). Result: No more corruption of internal state caused by calling PoolThreadCache.free() multuple times. --- .../java/io/netty/buffer/PoolThreadCache.java | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java index 3279da73d9e1..2ad397755edc 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java +++ b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java @@ -27,6 +27,7 @@ import java.nio.ByteBuffer; import java.util.Queue; +import java.util.concurrent.atomic.AtomicBoolean; /** * Acts a Thread cache for allocations. This implementation is moduled after @@ -54,6 +55,7 @@ final class PoolThreadCache { private final int numShiftsNormalDirect; private final int numShiftsNormalHeap; private final int freeSweepAllocationThreshold; + private final AtomicBoolean freed = new AtomicBoolean(); private int allocations; @@ -233,23 +235,28 @@ protected void finalize() throws Throwable { * Should be called if the Thread that uses this cache is about to exist to release resources out of the cache */ void free() { - int numFreed = free(tinySubPageDirectCaches) + - free(smallSubPageDirectCaches) + - free(normalDirectCaches) + - free(tinySubPageHeapCaches) + - free(smallSubPageHeapCaches) + - free(normalHeapCaches); - - if (numFreed > 0 && logger.isDebugEnabled()) { - logger.debug("Freed {} thread-local buffer(s) from thread: {}", numFreed, Thread.currentThread().getName()); - } + // As free() may be called either by the finalizer or by FastThreadLocal.onRemoval(...) we need to ensure + // we only call this one time. + if (freed.compareAndSet(false, true)) { + int numFreed = free(tinySubPageDirectCaches) + + free(smallSubPageDirectCaches) + + free(normalDirectCaches) + + free(tinySubPageHeapCaches) + + free(smallSubPageHeapCaches) + + free(normalHeapCaches); + + if (numFreed > 0 && logger.isDebugEnabled()) { + logger.debug("Freed {} thread-local buffer(s) from thread: {}", numFreed, + Thread.currentThread().getName()); + } - if (directArena != null) { - directArena.numThreadCaches.getAndDecrement(); - } + if (directArena != null) { + directArena.numThreadCaches.getAndDecrement(); + } - if (heapArena != null) { - heapArena.numThreadCaches.getAndDecrement(); + if (heapArena != null) { + heapArena.numThreadCaches.getAndDecrement(); + } } } From a137291ad161c970259d9a5d7cc206c026f08ee7 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 10 Jul 2018 00:42:37 -0400 Subject: [PATCH 086/417] =?UTF-8?q?Add=20OpenSslX509KeyManagerFactory=20wh?= =?UTF-8?q?ich=20makes=20it=20even=20easier=20for=20peopl=E2=80=A6=20(#808?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add OpenSslX509KeyManagerFactory which makes it even easier for people to get the maximum performance when using OpenSSL / LibreSSL / BoringSSL with netty. Motivation: To make it even easier for people to get the maximum performance when using native SSL we should provide our own KeyManagerFactory implementation that people can just use to configure their key material. Modifications: - Add OpenSslX509KeyManagerFactory which users can use for maximum performance with native SSL - Refactor some internal code to re-use logic and not duplicate it. Result: Easier to get the max performance out of native SSL implementation. --- .../ssl/OpenSslX509KeyManagerFactory.java | 218 ++++++++++++++++++ .../ReferenceCountedOpenSslClientContext.java | 13 +- .../ssl/ReferenceCountedOpenSslContext.java | 4 + .../ReferenceCountedOpenSslServerContext.java | 15 +- .../java/io/netty/handler/ssl/SslContext.java | 13 +- .../netty/handler/ssl/SslContextBuilder.java | 4 +- 6 files changed, 256 insertions(+), 11 deletions(-) create mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java new file mode 100644 index 000000000000..2d87aade7e2d --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java @@ -0,0 +1,218 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.util.ReferenceCountUtil; +import io.netty.util.internal.ObjectUtil; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.KeyManagerFactorySpi; +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.X509KeyManager; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.UnrecoverableKeyException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Special {@link KeyManagerFactory} that pre-compute the keymaterial used when {@link SslProvider#OPENSSL} or + * {@link SslProvider#OPENSSL_REFCNT} is used and so will improve handshake times and its performance. + * + * Because the keymaterial is pre-computed any modification to the {@link KeyStore} is ignored after + * {@link #init(KeyStore, char[])} is called. + * + * {@link #init(ManagerFactoryParameters)} is not supported by this implementation and so a call to it will always + * result in an {@link InvalidAlgorithmParameterException}. + */ +public final class OpenSslX509KeyManagerFactory extends KeyManagerFactory { + + private final OpenSslKeyManagerFactorySpi spi; + + public OpenSslX509KeyManagerFactory() { + this(newOpenSslKeyManagerFactorySpi(null)); + } + + public OpenSslX509KeyManagerFactory(Provider provider) { + this(newOpenSslKeyManagerFactorySpi(provider)); + } + + public OpenSslX509KeyManagerFactory(String algorithm, Provider provider) throws NoSuchAlgorithmException { + this(newOpenSslKeyManagerFactorySpi(algorithm, provider)); + } + + private OpenSslX509KeyManagerFactory(OpenSslKeyManagerFactorySpi spi) { + super(spi, spi.kmf.getProvider(), spi.kmf.getAlgorithm()); + this.spi = spi; + } + + private static OpenSslKeyManagerFactorySpi newOpenSslKeyManagerFactorySpi(Provider provider) { + try { + return newOpenSslKeyManagerFactorySpi(null, provider); + } catch (NoSuchAlgorithmException e) { + // This should never happen as we use the default algorithm. + throw new IllegalStateException(e); + } + } + + private static OpenSslKeyManagerFactorySpi newOpenSslKeyManagerFactorySpi(String algorithm, Provider provider) + throws NoSuchAlgorithmException { + if (algorithm == null) { + algorithm = KeyManagerFactory.getDefaultAlgorithm(); + } + return new OpenSslKeyManagerFactorySpi( + provider == null ? KeyManagerFactory.getInstance(algorithm) : + KeyManagerFactory.getInstance(algorithm, provider)); + } + + OpenSslKeyMaterialProvider newProvider() { + return spi.newProvider(); + } + + private static final class OpenSslKeyManagerFactorySpi extends KeyManagerFactorySpi { + final KeyManagerFactory kmf; + private volatile ProviderFactory providerFactory; + + OpenSslKeyManagerFactorySpi(KeyManagerFactory kmf) { + this.kmf = ObjectUtil.checkNotNull(kmf, "kmf"); + } + + @Override + protected synchronized void engineInit(KeyStore keyStore, char[] chars) + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { + if (providerFactory != null) { + throw new KeyStoreException("Already initialized"); + } + if (!keyStore.aliases().hasMoreElements()) { + throw new KeyStoreException("No aliases found"); + } + + kmf.init(keyStore, chars); + providerFactory = new ProviderFactory( + ReferenceCountedOpenSslContext.chooseX509KeyManager(kmf.getKeyManagers()), + password(chars), Collections.list(keyStore.aliases())); + } + + private static String password(char[] password) { + if (password == null || password.length == 0) { + return null; + } + return new String(password); + } + + @Override + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) + throws InvalidAlgorithmParameterException { + throw new InvalidAlgorithmParameterException("Not supported"); + } + + @Override + protected KeyManager[] engineGetKeyManagers() { + ProviderFactory providerFactory = this.providerFactory; + if (providerFactory == null) { + throw new IllegalStateException("engineInit(...) not called yet"); + } + return new KeyManager[] { providerFactory.keyManager }; + } + + OpenSslKeyMaterialProvider newProvider() { + ProviderFactory providerFactory = this.providerFactory; + if (providerFactory == null) { + throw new IllegalStateException("engineInit(...) not called yet"); + } + return providerFactory.newProvider(); + } + + private static final class ProviderFactory { + private final X509KeyManager keyManager; + private final String password; + private final Iterable aliases; + + ProviderFactory(X509KeyManager keyManager, String password, Iterable aliases) { + this.keyManager = keyManager; + this.password = password; + this.aliases = aliases; + } + + OpenSslKeyMaterialProvider newProvider() { + return new OpenSslPopulatedKeyMaterialProvider(keyManager, + password, aliases); + } + + /** + * {@link OpenSslKeyMaterialProvider} implementation that pre-compute the {@link OpenSslKeyMaterial} for + * all aliases. + */ + private static final class OpenSslPopulatedKeyMaterialProvider extends OpenSslKeyMaterialProvider { + private final Map materialMap; + + OpenSslPopulatedKeyMaterialProvider( + X509KeyManager keyManager, String password, Iterable aliases) { + super(keyManager, password); + materialMap = new HashMap(); + boolean initComplete = false; + try { + for (String alias: aliases) { + if (alias != null && !materialMap.containsKey(alias)) { + try { + materialMap.put(alias, super.chooseKeyMaterial( + UnpooledByteBufAllocator.DEFAULT, alias)); + } catch (Exception e) { + // Just store the exception and rethrow it when we try to choose the keymaterial + // for this alias later on. + materialMap.put(alias, e); + } + } + } + initComplete = true; + } finally { + if (!initComplete) { + destroy(); + } + } + if (materialMap.isEmpty()) { + throw new IllegalArgumentException("aliases must be non-empty"); + } + } + + @Override + OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception { + Object value = materialMap.get(alias); + if (value instanceof OpenSslKeyMaterial) { + return ((OpenSslKeyMaterial) value).retain(); + } else { + throw (Exception) value; + } + } + + @Override + void destroy() { + for (Object material: materialMap.values()) { + ReferenceCountUtil.release(material); + } + materialMap.clear(); + } + } + } + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java index 749c540a3afd..29815c429e99 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java @@ -102,9 +102,16 @@ static OpenSslSessionContext newSessionContext(ReferenceCountedOpenSslContext th } else { // javadocs state that keyManagerFactory has precedent over keyCertChain if (keyManagerFactory == null && keyCertChain != null) { - keyMaterialProvider = new OpenSslCachingKeyMaterialProvider( - chooseX509KeyManager(buildKeyManagerFactory(keyCertChain, key, keyPassword, null) - .getKeyManagers()), keyPassword); + char[] keyPasswordChars = keyStorePassword(keyPassword); + KeyStore ks = buildKeyStore(keyCertChain, key, keyPasswordChars); + if (ks.aliases().hasMoreElements()) { + keyManagerFactory = new OpenSslX509KeyManagerFactory(); + } else { + keyManagerFactory = new OpenSslCachingX509KeyManagerFactory( + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())); + } + keyManagerFactory.init(ks, keyPasswordChars); + keyMaterialProvider = providerFor(keyManagerFactory, keyPassword); } else if (keyManagerFactory != null) { keyMaterialProvider = providerFor(keyManagerFactory, keyPassword); } diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index 2a961ca024c1..4a0202267767 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -823,6 +823,10 @@ private static long newBIO(ByteBuf buffer) throws Exception { * ensure that the same material is always returned for the same alias. */ static OpenSslKeyMaterialProvider providerFor(KeyManagerFactory factory, String password) { + if (factory instanceof OpenSslX509KeyManagerFactory) { + return ((OpenSslX509KeyManagerFactory) factory).newProvider(); + } + X509KeyManager keyManager = chooseX509KeyManager(factory.getKeyManagers()); if (factory instanceof OpenSslCachingX509KeyManagerFactory) { // The user explicit used OpenSslCachingX509KeyManagerFactory which signals us that its fine to cache. diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java index b8f0254ef5f8..9f09393f01e3 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java @@ -121,12 +121,17 @@ static ServerContext newSessionContext(ReferenceCountedOpenSslContext thiz, long // javadocs state that keyManagerFactory has precedent over keyCertChain, and we must have a // keyManagerFactory for the server so build one if it is not specified. if (keyManagerFactory == null) { - keyMaterialProvider = new OpenSslCachingKeyMaterialProvider( - chooseX509KeyManager(buildKeyManagerFactory(keyCertChain, key, keyPassword, null) - .getKeyManagers()), keyPassword); - } else { - keyMaterialProvider = providerFor(keyManagerFactory, keyPassword); + char[] keyPasswordChars = keyStorePassword(keyPassword); + KeyStore ks = buildKeyStore(keyCertChain, key, keyPasswordChars); + if (ks.aliases().hasMoreElements()) { + keyManagerFactory = new OpenSslX509KeyManagerFactory(); + } else { + keyManagerFactory = new OpenSslCachingX509KeyManagerFactory( + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())); + } + keyManagerFactory.init(ks, keyPasswordChars); } + keyMaterialProvider = providerFor(keyManagerFactory, keyPassword); result.keyMaterialManager = new OpenSslKeyMaterialManager(keyMaterialProvider); } diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContext.java b/handler/src/main/java/io/netty/handler/ssl/SslContext.java index ef5c4bfdca79..c842a1461552 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java @@ -1154,8 +1154,15 @@ static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChainFile, String keyPassword, KeyManagerFactory kmf) throws KeyStoreException, NoSuchAlgorithmException, IOException, CertificateException, UnrecoverableKeyException { - char[] keyPasswordChars = keyPassword == null ? EmptyArrays.EMPTY_CHARS : keyPassword.toCharArray(); + char[] keyPasswordChars = keyStorePassword(keyPassword); KeyStore ks = buildKeyStore(certChainFile, key, keyPasswordChars); + return buildKeyManagerFactory(ks, keyAlgorithm, keyPasswordChars, kmf); + } + + static KeyManagerFactory buildKeyManagerFactory(KeyStore ks, + String keyAlgorithm, + char[] keyPasswordChars, KeyManagerFactory kmf) + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { // Set up key manager factory to use our key store if (kmf == null) { kmf = KeyManagerFactory.getInstance(keyAlgorithm); @@ -1164,4 +1171,8 @@ static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChainFile, return kmf; } + + static char[] keyStorePassword(String keyPassword) { + return keyPassword == null ? EmptyArrays.EMPTY_CHARS : keyPassword.toCharArray(); + } } diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java index 48617505af59..ae21440ce099 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java @@ -122,7 +122,7 @@ public static SslContextBuilder forServer( * Creates a builder for new server-side {@link SslContext}. * * If you use {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} consider using - * {@link OpenSslCachingX509KeyManagerFactory}. + * {@link OpenSslX509KeyManagerFactory} or {@link OpenSslCachingX509KeyManagerFactory}. * * @param keyManagerFactory non-{@code null} factory for server's private key * @see #keyManager(KeyManagerFactory) @@ -340,7 +340,7 @@ public SslContextBuilder keyManager(PrivateKey key, String keyPassword, X509Cert * you must use {@link #keyManager(File, File)} or {@link #keyManager(File, File, String)}. * * If you use {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} consider using - * {@link OpenSslCachingX509KeyManagerFactory}. + * {@link OpenSslX509KeyManagerFactory} or {@link OpenSslCachingX509KeyManagerFactory}. */ public SslContextBuilder keyManager(KeyManagerFactory keyManagerFactory) { if (forServer) { From 8ca5421bd2925b944a65a2377a68cdb575fc0733 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 10 Jul 2018 05:18:13 +0000 Subject: [PATCH 087/417] [maven-release-plugin] prepare release netty-4.1.26.Final --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 37 files changed, 75 insertions(+), 71 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 2544dd0fea17..e3b3216f06ac 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 9aa59bb9c1c8..968889021750 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.26.Final-SNAPSHOT + 4.1.26.Final pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.26.Final @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec-dns - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec-haproxy - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec-http - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec-http2 - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec-memcache - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec-mqtt - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec-redis - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec-smtp - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec-socks - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec-stomp - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-codec-xml - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-common - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-dev-tools - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-handler - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-handler-proxy - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-resolver - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-resolver-dns - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-transport - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-transport-rxtx - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-transport-sctp - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-transport-udt - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-example - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-all - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-transport-native-unix-common - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-transport-native-unix-common - 4.1.26.Final-SNAPSHOT + 4.1.26.Final linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.26.Final-SNAPSHOT + 4.1.26.Final osx-x86_64 io.netty netty-transport-native-epoll - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-transport-native-epoll - 4.1.26.Final-SNAPSHOT + 4.1.26.Final linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.26.Final-SNAPSHOT + 4.1.26.Final io.netty netty-transport-native-kqueue - 4.1.26.Final-SNAPSHOT + 4.1.26.Final osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index dac037a31ca3..93fa3a181785 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 3f7cc1630003..3ff1fa760def 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 85ec6f33b569..5f9dbb8b6e59 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index ab891b5dd0fd..e250b68f4145 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 83b75a4667b5..9649ad87d3b8 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index bd8697a242f4..952bbc01c6ed 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 0299e20851eb..19b0e6362702 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index d82cdd93f50a..42ad31cc0f23 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index 103bf429e901..5007bb6d15ce 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index bf6062f73e4d..44578171c80e 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 9adc0eee1cda..ec53b1b3f0c3 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index 146f67f7eb19..9f253585af65 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 8757fe31df2d..87345b4f4c7c 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-codec diff --git a/common/pom.xml b/common/pom.xml index 530365c37092..269d9fb78a90 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index f268b9341c18..c198f1715476 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.26.Final-SNAPSHOT + 4.1.26.Final Netty/Dev-Tools @@ -50,4 +50,8 @@ + + + netty-4.1.26.Final + diff --git a/example/pom.xml b/example/pom.xml index df4c9a222687..4b91eab8f2bd 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 5a649bd1f7dd..85878acef01e 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index 2f8b393c28a1..f440f6759f11 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index e03d8dbbea09..d2677699e936 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-microbench diff --git a/pom.xml b/pom.xml index 6523acbe0e0e..c7249ef2a59f 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.26.Final-SNAPSHOT + 4.1.26.Final Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.26.Final diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index a0089ade9b97..12499ffa44a5 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index b199bac951c2..b0c8d80413d5 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 55ccc1c34342..b5f2b4420cfb 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 989f3f980e65..6c41f7565aa5 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 372f34a9c9ca..5a47782c4061 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 9bcce3eb3c81..afbe57fbbf33 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-testsuite-osgi diff --git a/testsuite/pom.xml b/testsuite/pom.xml index c5806cf1b707..00544d4c3dee 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index bf5d3e60f1f9..e30e8609064d 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 865bac1c036f..b9e2d37588ab 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index ef5346759013..1ba8f59dc726 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 122f1de44f94..8b66bf30a600 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index a89807a080e5..64e0c21f4cde 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index e0975c1cc407..517df66454a9 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 414575294b9b..d3d3d3f0be72 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 2141c8f21216..4b1b6fd2ac34 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final-SNAPSHOT + 4.1.26.Final netty-transport From 7bb9e7eafe67eef252ef4c66db3e6e34fbaf069e Mon Sep 17 00:00:00 2001 From: root Date: Tue, 10 Jul 2018 05:21:24 +0000 Subject: [PATCH 088/417] [maven-release-plugin] prepare for next development iteration --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 37 files changed, 71 insertions(+), 75 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index e3b3216f06ac..d10a39fbbaf7 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 968889021750..c1fd166b05d5 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.26.Final + 4.1.27.Final-SNAPSHOT pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.26.Final + HEAD @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec-dns - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec-haproxy - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec-http - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec-http2 - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec-memcache - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec-mqtt - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec-redis - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec-smtp - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec-socks - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec-stomp - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-codec-xml - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-common - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-dev-tools - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-handler - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-handler-proxy - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-resolver - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-resolver-dns - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-transport - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-transport-rxtx - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-transport-sctp - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-transport-udt - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-example - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-all - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.26.Final + 4.1.27.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.26.Final + 4.1.27.Final-SNAPSHOT osx-x86_64 io.netty netty-transport-native-epoll - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-transport-native-epoll - 4.1.26.Final + 4.1.27.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.26.Final + 4.1.27.Final-SNAPSHOT io.netty netty-transport-native-kqueue - 4.1.26.Final + 4.1.27.Final-SNAPSHOT osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 93fa3a181785..dbd7dabbea59 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 3ff1fa760def..01b46f720bc5 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 5f9dbb8b6e59..2bf389d36e36 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index e250b68f4145..91d774b7dfdf 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 9649ad87d3b8..bdd7871359f0 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 952bbc01c6ed..21ae476c6287 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 19b0e6362702..546cfde9bca0 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 42ad31cc0f23..4b6395c184f5 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index 5007bb6d15ce..a538ebe388a9 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 44578171c80e..d27b978409f7 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index ec53b1b3f0c3..14eaf7b4260f 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index 9f253585af65..10bad8fdaf7d 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 87345b4f4c7c..c9de764e1b32 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-codec diff --git a/common/pom.xml b/common/pom.xml index 269d9fb78a90..06b1dba3f344 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index c198f1715476..e296d2ebdfa3 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.26.Final + 4.1.27.Final-SNAPSHOT Netty/Dev-Tools @@ -50,8 +50,4 @@ - - - netty-4.1.26.Final - diff --git a/example/pom.xml b/example/pom.xml index 4b91eab8f2bd..cd828525ad73 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 85878acef01e..0c67587a3539 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index f440f6759f11..ec9069b2767c 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index d2677699e936..88ea0ca9139e 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-microbench diff --git a/pom.xml b/pom.xml index c7249ef2a59f..099a8a2244bf 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.26.Final + 4.1.27.Final-SNAPSHOT Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.26.Final + HEAD diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index 12499ffa44a5..bdb4fafca478 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index b0c8d80413d5..522d9de5a6b3 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index b5f2b4420cfb..44a2e9eac718 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 6c41f7565aa5..cdfc342fca82 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 5a47782c4061..ce6bbf8b6f87 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index afbe57fbbf33..fc548ceeeed6 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-testsuite-osgi diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 00544d4c3dee..26d77af1a6dd 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index e30e8609064d..a4992f83a140 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index b9e2d37588ab..a5fa0ed64d0d 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 1ba8f59dc726..ef717051887a 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 8b66bf30a600..106f176cd6e3 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index 64e0c21f4cde..d00f577d7513 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 517df66454a9..1ac4c30afd73 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index d3d3d3f0be72..eed52ce60b99 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 4b1b6fd2ac34..0997675ecb58 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.26.Final + 4.1.27.Final-SNAPSHOT netty-transport From 4b9125f9610ad7a108dc16f5e41da0ad8a044080 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 11 Jul 2018 10:18:18 +0100 Subject: [PATCH 089/417] Correctly release message in MemcacheClientHandler that is used in the memcache example. (#8119) Motivation: MemcacheClientHandler.channelRead(...) need to release the frame after it prints out its content to not introduce a memory leak. Modifications: Call release() on the frame. Result: Example has no leak any more. --- .../io/netty/example/memcache/binary/MemcacheClientHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/example/src/main/java/io/netty/example/memcache/binary/MemcacheClientHandler.java b/example/src/main/java/io/netty/example/memcache/binary/MemcacheClientHandler.java index c2ee142f7b6e..1864ad33c267 100644 --- a/example/src/main/java/io/netty/example/memcache/binary/MemcacheClientHandler.java +++ b/example/src/main/java/io/netty/example/memcache/binary/MemcacheClientHandler.java @@ -69,6 +69,7 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) public void channelRead(ChannelHandlerContext ctx, Object msg) { FullBinaryMemcacheResponse res = (FullBinaryMemcacheResponse) msg; System.out.println(res.content().toString(CharsetUtil.UTF_8)); + res.release(); } @Override From cf713d03685feadeca382988c11dfdfe85c54c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=B6=E6=97=A0=E4=B8=A4=E4=B8=B6?= <442367943@qq.com> Date: Wed, 11 Jul 2018 17:19:03 +0800 Subject: [PATCH 090/417] Remove extra 'should' word in docs of continueReading() method --- .../src/main/java/io/netty/channel/RecvByteBufAllocator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/src/main/java/io/netty/channel/RecvByteBufAllocator.java b/transport/src/main/java/io/netty/channel/RecvByteBufAllocator.java index 1c65f0c9b47c..4145c90fe1c4 100644 --- a/transport/src/main/java/io/netty/channel/RecvByteBufAllocator.java +++ b/transport/src/main/java/io/netty/channel/RecvByteBufAllocator.java @@ -96,7 +96,7 @@ interface Handle { int attemptedBytesRead(); /** - * Determine if the current read loop should should continue. + * Determine if the current read loop should continue. * @return {@code true} if the read loop should continue reading. {@code false} if the read loop is complete. */ boolean continueReading(); From 301e22eafb659a09be23b47ca158c731da0b0f6f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 11 Jul 2018 10:19:20 +0100 Subject: [PATCH 091/417] Fix incorrect code in javadocs of ChannelHandler. (#8115) Motivation: Some code that was shown as part of the ChannelHandler javadoc was not 100 % correct and used some constructs that we used in netty 3. Also we never called flush() in the code which is a bad example for users. Modifications: - Remove netty 3 code references - Replace channel.write(...) with ctx.writeAndFlush(...) Result: More correct code in the javadocs. --- .../src/main/java/io/netty/channel/ChannelHandler.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/ChannelHandler.java b/transport/src/main/java/io/netty/channel/ChannelHandler.java index 898f7523bcf2..f9401080eb48 100644 --- a/transport/src/main/java/io/netty/channel/ChannelHandler.java +++ b/transport/src/main/java/io/netty/channel/ChannelHandler.java @@ -73,13 +73,12 @@ * * {@code @Override} * public void channelRead0({@link ChannelHandlerContext} ctx, Message message) { - * {@link Channel} ch = e.getChannel(); * if (message instanceof LoginMessage) { * authenticate((LoginMessage) message); * loggedIn = true; * } else (message instanceof GetDataMessage) { * if (loggedIn) { - * ch.write(fetchSecret((GetDataMessage) message)); + * ctx.writeAndFlush(fetchSecret((GetDataMessage) message)); * } else { * fail(); * } @@ -123,13 +122,12 @@ * {@code @Override} * public void channelRead({@link ChannelHandlerContext} ctx, Message message) { * {@link Attribute}<{@link Boolean}> attr = ctx.attr(auth); - * {@link Channel} ch = ctx.channel(); * if (message instanceof LoginMessage) { * authenticate((LoginMessage) o); * attr.set(true); * } else (message instanceof GetDataMessage) { * if (Boolean.TRUE.equals(attr.get())) { - * ch.write(fetchSecret((GetDataMessage) o)); + * ctx.writeAndFlush(fetchSecret((GetDataMessage) o)); * } else { * fail(); * } From 93d2807ff0eb6d886a4da8fc52a97ea7bbc056b5 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 11 Jul 2018 10:19:37 +0100 Subject: [PATCH 092/417] Auto-detect Log4J2 for logging if on the class-path (#8109) Motivation: https://github.com/netty/netty/pull/5047 added Log4J2 support but missed to add code to try to auto-detect it. Modifications: Try to use Log4JLoggerFactory by default. Result: Fixes https://github.com/netty/netty/issues/8107. --- .../internal/logging/InternalLoggerFactory.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/logging/InternalLoggerFactory.java b/common/src/main/java/io/netty/util/internal/logging/InternalLoggerFactory.java index 9f85e3646b40..12c1b5a4477d 100644 --- a/common/src/main/java/io/netty/util/internal/logging/InternalLoggerFactory.java +++ b/common/src/main/java/io/netty/util/internal/logging/InternalLoggerFactory.java @@ -41,13 +41,18 @@ private static InternalLoggerFactory newDefaultFactory(String name) { try { f = new Slf4JLoggerFactory(true); f.newInstance(name).debug("Using SLF4J as the default logging framework"); - } catch (Throwable t1) { + } catch (Throwable ignore1) { try { f = Log4JLoggerFactory.INSTANCE; f.newInstance(name).debug("Using Log4J as the default logging framework"); - } catch (Throwable t2) { - f = JdkLoggerFactory.INSTANCE; - f.newInstance(name).debug("Using java.util.logging as the default logging framework"); + } catch (Throwable ignore2) { + try { + f = Log4J2LoggerFactory.INSTANCE; + f.newInstance(name).debug("Using Log4J2 as the default logging framework"); + } catch (Throwable ignore3) { + f = JdkLoggerFactory.INSTANCE; + f.newInstance(name).debug("Using java.util.logging as the default logging framework"); + } } } return f; From 8186c9aaeab240dfe500eb2d313467283522288b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 11 Jul 2018 10:21:17 +0100 Subject: [PATCH 093/417] Fix length calculation in AsciiString.indexOf(...) and so eliminate ArrayIndexOutOfBoundsException. (#8116) Motivation: We incorrectly calculated the length that was used for our for loop in AsciiString.indexOf(...). This lead to a possible ArrayIndexOutOfBoundsException. Modifications: - Not include the start in the length calculation - Add unit test. Result: Fixes https://github.com/netty/netty/issues/8112. --- .../src/main/java/io/netty/util/AsciiString.java | 2 +- .../io/netty/util/AsciiStringCharacterTest.java | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/io/netty/util/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java index 58a6f45a9df3..2730923f8dd3 100644 --- a/common/src/main/java/io/netty/util/AsciiString.java +++ b/common/src/main/java/io/netty/util/AsciiString.java @@ -722,7 +722,7 @@ public int indexOf(char ch, int start) { } final byte chAsByte = c2b0(ch); - final int len = offset + start + length; + final int len = offset + length; for (int i = start + offset; i < len; ++i) { if (value[i] == chAsByte) { return i - offset; diff --git a/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java b/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java index fe2ec301b0f8..cdd962d3546e 100644 --- a/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java +++ b/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java @@ -398,4 +398,18 @@ public void testSubStringHashCode() { //two "123"s assertEquals(AsciiString.hashCode("123"), AsciiString.hashCode("a123".substring(1))); } + + @Test + public void testIndexOf() { + AsciiString foo = AsciiString.of("This is a test"); + int i1 = foo.indexOf(' ', 0); + assertEquals(4, i1); + int i2 = foo.indexOf(' ', i1 + 1); + assertEquals(7, i2); + int i3 = foo.indexOf(' ', i2 + 1); + assertEquals(9, i3); + assertTrue(i3 + 1 < foo.length()); + int i4 = foo.indexOf(' ', i3 + 1); + assertEquals(i4, -1); + } } From df08467d7c3589a83411bb99e1e347b666c5241f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 11 Jul 2018 15:19:37 +0100 Subject: [PATCH 094/417] Fix possible NPE introduced by a137291ad161c970259d9a5d7cc206c026f08ee7 when using SslProvider.OPENSSL and init via files or OpenSslX509KeyManagerFactory (#8126) Motivation: a137291ad161c970259d9a5d7cc206c026f08ee7 introduced a way to get the most speed out of OpenSSL by not only caching keymaterial but pre-compute these. The problem was we missed to check for null before doing an instanceof check and then a cast which could lead to a NPE as we tried to cast null to Exception and throw it. Modifications: Add null check and unit test. Result: No more NPE when keymaterial was not found for requested alias. --- .../ssl/OpenSslX509KeyManagerFactory.java | 7 +++- ...OpenSslCachingKeyMaterialProviderTest.java | 15 ++------ .../ssl/OpenSslKeyMaterialProviderTest.java | 22 ++--------- ...nSslX509KeyManagerFactoryProviderTest.java | 38 +++++++++++++++++++ 4 files changed, 51 insertions(+), 31 deletions(-) create mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactoryProviderTest.java diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java index 2d87aade7e2d..e47625cabc9d 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java @@ -198,11 +198,14 @@ private static final class OpenSslPopulatedKeyMaterialProvider extends OpenSslKe @Override OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception { Object value = materialMap.get(alias); + if (value == null) { + // There is no keymaterial for the requested alias, return null + return null; + } if (value instanceof OpenSslKeyMaterial) { return ((OpenSslKeyMaterial) value).retain(); - } else { - throw (Exception) value; } + throw (Exception) value; } @Override diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProviderTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProviderTest.java index e284da15179a..cfb4557fc237 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProviderTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProviderTest.java @@ -20,7 +20,6 @@ import org.junit.Test; import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.X509KeyManager; import static org.junit.Assert.*; @@ -32,8 +31,9 @@ protected KeyManagerFactory newKeyManagerFactory() throws Exception { } @Override - protected OpenSslKeyMaterialProvider newMaterialProvider(X509KeyManager manager, String password) { - return new OpenSslCachingKeyMaterialProvider(manager, password); + protected OpenSslKeyMaterialProvider newMaterialProvider(KeyManagerFactory factory, String password) { + return new OpenSslCachingKeyMaterialProvider(ReferenceCountedOpenSslContext.chooseX509KeyManager( + factory.getKeyManagers()), password); } @Override @@ -41,16 +41,9 @@ protected void assertRelease(OpenSslKeyMaterial material) { Assert.assertFalse(material.release()); } - @Override - protected Class providerClass() { - return OpenSslCachingKeyMaterialProvider.class; - } - @Test public void testMaterialCached() throws Exception { - X509KeyManager manager = ReferenceCountedOpenSslContext.chooseX509KeyManager( - newKeyManagerFactory().getKeyManagers()); - OpenSslKeyMaterialProvider provider = newMaterialProvider(manager, PASSWORD); + OpenSslKeyMaterialProvider provider = newMaterialProvider(newKeyManagerFactory(), PASSWORD); OpenSslKeyMaterial material = provider.chooseKeyMaterial(UnpooledByteBufAllocator.DEFAULT, EXISTING_ALIAS); assertNotNull(material); diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialProviderTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialProviderTest.java index 651db93f089b..5b793fe87b17 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialProviderTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialProviderTest.java @@ -20,7 +20,6 @@ import org.junit.Test; import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.X509KeyManager; import java.security.KeyStore; @@ -49,12 +48,9 @@ protected KeyManagerFactory newKeyManagerFactory() throws Exception { return kmf; } - protected OpenSslKeyMaterialProvider newMaterialProvider(X509KeyManager manager, String password) { - return new OpenSslKeyMaterialProvider(manager, password); - } - - protected Class providerClass() { - return OpenSslKeyMaterialProvider.class; + protected OpenSslKeyMaterialProvider newMaterialProvider(KeyManagerFactory factory, String password) { + return new OpenSslKeyMaterialProvider(ReferenceCountedOpenSslContext.chooseX509KeyManager( + factory.getKeyManagers()), password); } protected void assertRelease(OpenSslKeyMaterial material) { @@ -63,9 +59,7 @@ protected void assertRelease(OpenSslKeyMaterial material) { @Test public void testChooseKeyMaterial() throws Exception { - X509KeyManager manager = ReferenceCountedOpenSslContext.chooseX509KeyManager( - newKeyManagerFactory().getKeyManagers()); - OpenSslKeyMaterialProvider provider = newMaterialProvider(manager, PASSWORD); + OpenSslKeyMaterialProvider provider = newMaterialProvider(newKeyManagerFactory(), PASSWORD); OpenSslKeyMaterial nonExistingMaterial = provider.chooseKeyMaterial( UnpooledByteBufAllocator.DEFAULT, NON_EXISTING_ALIAS); assertNull(nonExistingMaterial); @@ -78,12 +72,4 @@ public void testChooseKeyMaterial() throws Exception { provider.destroy(); } - - @Test - public void testChooseTheCorrectProvider() throws Exception { - OpenSslKeyMaterialProvider provider = ReferenceCountedOpenSslContext.providerFor( - newKeyManagerFactory(), PASSWORD); - assertEquals(providerClass(), provider.getClass()); - provider.destroy(); - } } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactoryProviderTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactoryProviderTest.java new file mode 100644 index 000000000000..6afe5e4e72f4 --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactoryProviderTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import javax.net.ssl.KeyManagerFactory; +import java.security.KeyStore; + +public class OpenSslX509KeyManagerFactoryProviderTest extends OpenSslCachingKeyMaterialProviderTest { + + @Override + protected KeyManagerFactory newKeyManagerFactory() throws Exception { + char[] password = PASSWORD.toCharArray(); + final KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load(getClass().getResourceAsStream("mutual_auth_server.p12"), password); + + OpenSslX509KeyManagerFactory kmf = new OpenSslX509KeyManagerFactory(); + kmf.init(keystore, password); + return kmf; + } + + @Override + protected OpenSslKeyMaterialProvider newMaterialProvider(KeyManagerFactory kmf, String password) { + return ((OpenSslX509KeyManagerFactory) kmf).newProvider(); + } +} From 1c16519ac86d9ef9919ac96afbee4be9b428d0c9 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 11 Jul 2018 15:37:21 +0000 Subject: [PATCH 095/417] [maven-release-plugin] prepare release netty-4.1.27.Final --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 37 files changed, 75 insertions(+), 71 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index d10a39fbbaf7..c9552e35aee4 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-all diff --git a/bom/pom.xml b/bom/pom.xml index c1fd166b05d5..7f2149cfb91f 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.27.Final-SNAPSHOT + 4.1.27.Final pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.27.Final @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec-dns - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec-haproxy - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec-http - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec-http2 - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec-memcache - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec-mqtt - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec-redis - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec-smtp - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec-socks - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec-stomp - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-codec-xml - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-common - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-dev-tools - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-handler - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-handler-proxy - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-resolver - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-resolver-dns - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-transport - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-transport-rxtx - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-transport-sctp - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-transport-udt - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-example - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-all - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-transport-native-unix-common - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-transport-native-unix-common - 4.1.27.Final-SNAPSHOT + 4.1.27.Final linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.27.Final-SNAPSHOT + 4.1.27.Final osx-x86_64 io.netty netty-transport-native-epoll - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-transport-native-epoll - 4.1.27.Final-SNAPSHOT + 4.1.27.Final linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.27.Final-SNAPSHOT + 4.1.27.Final io.netty netty-transport-native-kqueue - 4.1.27.Final-SNAPSHOT + 4.1.27.Final osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index dbd7dabbea59..0c946a7d1789 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 01b46f720bc5..140390700328 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 2bf389d36e36..acabb01dd838 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 91d774b7dfdf..fdef84903744 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index bdd7871359f0..bb996a6a5114 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 21ae476c6287..966690ea18f7 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 546cfde9bca0..fa46f36c855d 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 4b6395c184f5..4f17646a03c0 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index a538ebe388a9..fc245badfdca 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index d27b978409f7..b4c3a64bb144 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 14eaf7b4260f..a0a52b07b739 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index 10bad8fdaf7d..e182c4d468aa 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index c9de764e1b32..e345659e496a 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-codec diff --git a/common/pom.xml b/common/pom.xml index 06b1dba3f344..4d697d124fab 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index e296d2ebdfa3..78d696b30fd0 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.27.Final-SNAPSHOT + 4.1.27.Final Netty/Dev-Tools @@ -50,4 +50,8 @@ + + + netty-4.1.27.Final + diff --git a/example/pom.xml b/example/pom.xml index cd828525ad73..749ff6d7c283 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 0c67587a3539..af821df69480 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index ec9069b2767c..69dc13679d25 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 88ea0ca9139e..92b5eeb9816a 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-microbench diff --git a/pom.xml b/pom.xml index 099a8a2244bf..7c840e9904c4 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.27.Final-SNAPSHOT + 4.1.27.Final Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.27.Final diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index bdb4fafca478..b87a4e963a6f 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index 522d9de5a6b3..b770c6eabc6e 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 44a2e9eac718..aa2cf363746e 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index cdfc342fca82..f32259269d78 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index ce6bbf8b6f87..8f903b2fac03 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index fc548ceeeed6..c879b3957755 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-testsuite-osgi diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 26d77af1a6dd..cb03dc393b40 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index a4992f83a140..3d065f1cf8c8 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index a5fa0ed64d0d..ddf63e253ecb 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index ef717051887a..15a27c54a4aa 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 106f176cd6e3..5c47c9fb3aca 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index d00f577d7513..161a8afbae5d 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 1ac4c30afd73..9737ac374aad 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index eed52ce60b99..e5e9d891cd18 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 0997675ecb58..e2885b0b2603 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.27.Final netty-transport From b4dbdc203658b5c2390d9e3177e586b67d3880aa Mon Sep 17 00:00:00 2001 From: root Date: Wed, 11 Jul 2018 15:37:40 +0000 Subject: [PATCH 096/417] [maven-release-plugin] prepare for next development iteration --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 37 files changed, 71 insertions(+), 75 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index c9552e35aee4..5c63cf3fc731 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 7f2149cfb91f..6e14a4b92adc 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.27.Final + 4.1.28.Final-SNAPSHOT pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.27.Final + HEAD @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec-dns - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec-haproxy - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec-http - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec-http2 - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec-memcache - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec-mqtt - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec-redis - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec-smtp - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec-socks - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec-stomp - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-codec-xml - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-common - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-dev-tools - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-handler - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-handler-proxy - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-resolver - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-resolver-dns - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-transport - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-transport-rxtx - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-transport-sctp - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-transport-udt - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-example - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-all - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.27.Final + 4.1.28.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.27.Final + 4.1.28.Final-SNAPSHOT osx-x86_64 io.netty netty-transport-native-epoll - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-transport-native-epoll - 4.1.27.Final + 4.1.28.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.27.Final + 4.1.28.Final-SNAPSHOT io.netty netty-transport-native-kqueue - 4.1.27.Final + 4.1.28.Final-SNAPSHOT osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 0c946a7d1789..00e38d31b322 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 140390700328..1e56d2f8e4c0 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index acabb01dd838..4830d9ce2dce 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index fdef84903744..35e7a347778c 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index bb996a6a5114..e3e97a74663d 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 966690ea18f7..146dceb2c5ad 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index fa46f36c855d..d8042bd13513 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 4f17646a03c0..8879945b4456 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index fc245badfdca..f3186748e5db 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index b4c3a64bb144..ef4cecc2b61a 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index a0a52b07b739..4117dc22f838 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index e182c4d468aa..425a9ca75c1e 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index e345659e496a..eec11b9bd24d 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-codec diff --git a/common/pom.xml b/common/pom.xml index 4d697d124fab..ba924b10805c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 78d696b30fd0..4160ad1080c0 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.27.Final + 4.1.28.Final-SNAPSHOT Netty/Dev-Tools @@ -50,8 +50,4 @@ - - - netty-4.1.27.Final - diff --git a/example/pom.xml b/example/pom.xml index 749ff6d7c283..d9b907df376e 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index af821df69480..1f589732dfe1 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index 69dc13679d25..2299f7ba1572 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 92b5eeb9816a..894ba0d2b943 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-microbench diff --git a/pom.xml b/pom.xml index 7c840e9904c4..5a37f1562ad2 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.27.Final + 4.1.28.Final-SNAPSHOT Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.27.Final + HEAD diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index b87a4e963a6f..0448ef8ab840 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index b770c6eabc6e..8fcf4c836bb6 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index aa2cf363746e..41a7964144b8 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index f32259269d78..6708470fbcdc 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 8f903b2fac03..de7792d4dcff 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index c879b3957755..3680714d4f8a 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-testsuite-osgi diff --git a/testsuite/pom.xml b/testsuite/pom.xml index cb03dc393b40..6f20bfd96b11 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 3d065f1cf8c8..499da0168a69 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index ddf63e253ecb..1af0f8cf7436 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 15a27c54a4aa..eb46ffcdda1e 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 5c47c9fb3aca..4e5032f4961c 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index 161a8afbae5d..46aaae2f39e9 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 9737ac374aad..146c45b1b024 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index e5e9d891cd18..3695dbb20c06 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index e2885b0b2603..c3361fa3f841 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final + 4.1.28.Final-SNAPSHOT netty-transport From 38d5ae93aacd284b437fab777f75e474da1267f8 Mon Sep 17 00:00:00 2001 From: sullis Date: Wed, 11 Jul 2018 12:02:57 -0700 Subject: [PATCH 097/417] remove Travis build file (.travis.yml) (#8128) Motivation: the Netty project does not use Travis CI. Modification: Remove .travis.yml Result: No more Travis. --- .travis.yml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b35e822464d3..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: java -jdk: - - oraclejdk7 - - openjdk7 -branches: - only: - - master - - 3 - - 3.5 -before_install: 'mvn -version' -install: 'mvn clean install -Pfull -DskipTests' - From d67d639f5f4de11f32dc15abcd0e18bad5a24a21 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 11 Jul 2018 20:03:59 +0100 Subject: [PATCH 098/417] Add integration test for shading native libraries. (#8123) Motivation: It's easy to break the support for shading native libs as shown in https://github.com/netty/netty/issues/8090. We should have some testing to ensure all works as expected. Modification: Add new testsuite which verifies that shading our native transports work as expected. Result: Include test to verify shading of native code. --- pom.xml | 1 + testsuite-shading/pom.xml | 252 ++++++++++++++++++ .../io/netty/testsuite/shading/ShadingIT.java | 36 +++ 3 files changed, 289 insertions(+) create mode 100644 testsuite-shading/pom.xml create mode 100644 testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java diff --git a/pom.xml b/pom.xml index 5a37f1562ad2..2be97b968ba7 100644 --- a/pom.xml +++ b/pom.xml @@ -273,6 +273,7 @@ testsuite-autobahn testsuite-http2 testsuite-osgi + testsuite-shading microbench bom diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml new file mode 100644 index 000000000000..e214bcaf7809 --- /dev/null +++ b/testsuite-shading/pom.xml @@ -0,0 +1,252 @@ + + + + + 4.0.0 + + io.netty + netty-parent + 4.1.27.Final-SNAPSHOT + + + netty-testsuite-shading + jar + + Netty/Testsuite/Shading + + + ${project.build.directory}/src + ${project.build.directory}/versions + ${project.build.directory}/classes-shaded + ${classesShadedDir}/META-INF/native + shaded + ${project.artifactId}-${project.version}.jar + io.netty. + + + + + + kr.motd.maven + os-maven-plugin + 1.6.0 + + + + + + maven-deploy-plugin + + true + + + + + + + junit + junit + + + + + mac + + + mac + + + + netty_transport_native_kqueue_${os.detected.arch}.jnilib + + + + ${project.groupId} + netty-transport-native-kqueue + ${project.version} + ${jni.classifier} + compile + + + + + + + maven-shade-plugin + + + package + + shade + + + + + ${project.groupId} + + + + + ${shadedPackagePrefix} + ${shadingPrefix}.${shadedPackagePrefix} + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + unpack-jar-features + package + + run + + + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${shadingPrefix} + + + + + package + + integration-test + + + + + + + + + linux + + + linux + + + + netty_transport_native_epoll_${os.detected.arch}.so + + + + ${project.groupId} + netty-transport-native-epoll + ${project.version} + ${jni.classifier} + compile + + + + + + + maven-shade-plugin + + + package + + shade + + + + + ${project.groupId} + + + + + ${shadedPackagePrefix} + ${shadingPrefix}.${shadedPackagePrefix} + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + unpack-jar-features + package + + run + + + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${shadingPrefix} + + + + + package + + integration-test + + + + + + + + + + diff --git a/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java new file mode 100644 index 000000000000..21d02c1ce844 --- /dev/null +++ b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java @@ -0,0 +1,36 @@ +/* + * Copyright 2018 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: + * + * http://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.testsuite.shading; + +import io.netty.util.internal.PlatformDependent; +import org.junit.Test; + +import java.lang.reflect.Method; + +public class ShadingIT { + + @Test + public void testShadingNativeLibs() throws Exception { + String shadingPrefix = System.getProperty("shadingPrefix"); + final Class clazz = Class.forName(shadingPrefix + '.' + className()); + Method method = clazz.getMethod("ensureAvailability"); + method.invoke(null); + } + + private static String className() { + return PlatformDependent.isOsx() ? "io.netty.channel.kqueue.KQueue" : "io.netty.channel.epoll.Epoll"; + } +} From 77ec8397927e3ceb9b9a447a74e718f625ed9976 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 11 Jul 2018 22:13:48 +0200 Subject: [PATCH 099/417] Fix parent version number used by d67d639f5f4de11f32dc15abcd0e18bad5a24a21 --- testsuite-shading/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index e214bcaf7809..b3997bf2f41c 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.27.Final-SNAPSHOT + 4.1.28.Final-SNAPSHOT netty-testsuite-shading From 952eeb8e1e3706b09d4d3f32f16d7f0e5c540cb5 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 25 Jul 2018 12:32:28 +0800 Subject: [PATCH 100/417] =?UTF-8?q?Support=20the=20usage=20of=20SocketOpti?= =?UTF-8?q?on=20when=20nio=20is=20used=20and=20the=20java=20versi=E2=80=A6?= =?UTF-8?q?=20(#8085)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support the usage of SocketOption when nio is used and the java version >= 7. Motivation: The JDK uses SocketOption since java7 to support configuration options on the underyling Channel. We should allow to create a ChannelOption from a given SocketOption if nio is used. This also allows us to expose the same featureset in terms of configuration as the java nio implementation does without any extra effort. Modifications: - Add NioChannelOption which allows to wrap an existing SocketOption which then can be applied to the nio transport. - Add test-cases Result: Support the same configuration options as the JDK. Also fixes https://github.com/netty/netty/issues/8072. --- pom.xml | 2 + .../channel/socket/nio/NioChannelOption.java | 82 +++++++++++++++++++ .../socket/nio/NioDatagramChannelConfig.java | 27 ++++++ .../socket/nio/NioServerSocketChannel.java | 31 +++++++ .../channel/socket/nio/NioSocketChannel.java | 32 +++++++- .../socket/nio/AbstractNioChannelTest.java | 69 ++++++++++++++++ .../nio/NioDatagramChannelTest.java | 24 +++++- .../nio/NioServerSocketChannelTest.java | 20 ++++- .../nio/NioSocketChannelTest.java | 25 +++++- 9 files changed, 303 insertions(+), 9 deletions(-) create mode 100644 transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java create mode 100644 transport/src/test/java/io/netty/channel/socket/nio/AbstractNioChannelTest.java rename transport/src/test/java/io/netty/channel/{ => socket}/nio/NioDatagramChannelTest.java (78%) rename transport/src/test/java/io/netty/channel/{ => socket}/nio/NioSocketChannelTest.java (94%) diff --git a/pom.xml b/pom.xml index 2be97b968ba7..76412c6b2c33 100644 --- a/pom.xml +++ b/pom.xml @@ -702,6 +702,8 @@ java.nio.channels.SocketChannel java.net.StandardProtocolFamily java.nio.channels.spi.SelectorProvider + java.net.SocketOption + java.nio.channels.NetworkChannel sun.security.x509.AlgorithmId diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java b/transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java new file mode 100644 index 000000000000..7d94b99706ef --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java @@ -0,0 +1,82 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.socket.nio; + +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelOption; + +import java.io.IOException; +import java.net.SocketOption; +import java.nio.channels.NetworkChannel; +import java.util.Set; + +/** + * Provides {@link ChannelOption} over a given {@link SocketOption} which is then passed through the underlying + * {@link NetworkChannel}. + */ +public final class NioChannelOption extends ChannelOption { + + private final SocketOption option; + + @SuppressWarnings("deprecation") + private NioChannelOption(SocketOption option) { + super(option.name()); + this.option = option; + } + + /** + * Returns a {@link ChannelOption} for the given {@link SocketOption}. + */ + public static ChannelOption of(SocketOption option) { + return new NioChannelOption(option); + } + + // Internal helper methods to remove code duplication between Nio*Channel implementations. + static boolean setOption(NetworkChannel channel, NioChannelOption option, T value) { + if (!channel.supportedOptions().contains(option.option)) { + return false; + } + try { + channel.setOption(option.option, value); + return true; + } catch (IOException e) { + throw new ChannelException(e); + } + } + + static T getOption(NetworkChannel channel, NioChannelOption option) { + if (!channel.supportedOptions().contains(option.option)) { + return null; + } + try { + return channel.getOption(option.option); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @SuppressWarnings("unchecked") + static ChannelOption[] getOptions(NetworkChannel channel) { + Set> supportedOpts = channel.supportedOptions(); + ChannelOption[] extraOpts = new ChannelOption[supportedOpts.size()]; + + int i = 0; + for (SocketOption opt : supportedOpts) { + extraOpts[i++] = new NioChannelOption(opt); + } + return extraOpts; + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannelConfig.java index 92cf677f7cb7..7e109af556e6 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannelConfig.java @@ -16,6 +16,7 @@ package io.netty.channel.socket.nio; import io.netty.channel.ChannelException; +import io.netty.channel.ChannelOption; import io.netty.channel.socket.DatagramChannelConfig; import io.netty.channel.socket.DefaultDatagramChannelConfig; import io.netty.util.internal.PlatformDependent; @@ -27,6 +28,7 @@ import java.net.SocketException; import java.nio.channels.DatagramChannel; import java.util.Enumeration; +import java.util.Map; /** * The default {@link NioDatagramChannelConfig} implementation. @@ -205,4 +207,29 @@ private void setOption0(Object option, Object value) { } } } + + @Override + public boolean setOption(ChannelOption option, T value) { + if (PlatformDependent.javaVersion() >= 7 && option instanceof NioChannelOption) { + return NioChannelOption.setOption(javaChannel, (NioChannelOption) option, value); + } + return super.setOption(option, value); + } + + @Override + public T getOption(ChannelOption option) { + if (PlatformDependent.javaVersion() >= 7 && option instanceof NioChannelOption) { + return NioChannelOption.getOption(javaChannel, (NioChannelOption) option); + } + return super.getOption(option); + } + + @SuppressWarnings("unchecked") + @Override + public Map, Object> getOptions() { + if (PlatformDependent.javaVersion() >= 7) { + return getOptions(super.getOptions(), NioChannelOption.getOptions(javaChannel)); + } + return super.getOptions(); + } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java index f2d6f16f6567..128e531e56b5 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java @@ -17,6 +17,7 @@ import io.netty.channel.ChannelException; import io.netty.channel.ChannelMetadata; +import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOutboundBuffer; import io.netty.util.internal.SocketUtils; import io.netty.channel.nio.AbstractNioMessageChannel; @@ -35,6 +36,7 @@ import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; import java.util.List; +import java.util.Map; /** * A {@link io.netty.channel.socket.ServerSocketChannel} implementation which uses @@ -199,6 +201,35 @@ private NioServerSocketChannelConfig(NioServerSocketChannel channel, ServerSocke protected void autoReadCleared() { clearReadPending(); } + + @Override + public boolean setOption(ChannelOption option, T value) { + if (PlatformDependent.javaVersion() >= 7 && option instanceof NioChannelOption) { + return NioChannelOption.setOption(jdkChannel(), (NioChannelOption) option, value); + } + return super.setOption(option, value); + } + + @Override + public T getOption(ChannelOption option) { + if (PlatformDependent.javaVersion() >= 7 && option instanceof NioChannelOption) { + return NioChannelOption.getOption(jdkChannel(), (NioChannelOption) option); + } + return super.getOption(option); + } + + @SuppressWarnings("unchecked") + @Override + public Map, Object> getOptions() { + if (PlatformDependent.javaVersion() >= 7) { + return getOptions(super.getOptions(), NioChannelOption.getOptions(jdkChannel())); + } + return super.getOptions(); + } + + private ServerSocketChannel jdkChannel() { + return ((NioServerSocketChannel) channel).javaChannel(); + } } // Override just to to be able to call directly via unit tests. diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java index e5010ac3b572..74431791ce40 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java @@ -20,6 +20,7 @@ import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOutboundBuffer; import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoop; @@ -44,6 +45,7 @@ import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; +import java.util.Map; import java.util.concurrent.Executor; import static io.netty.channel.internal.ChannelUtils.MAX_BYTES_PER_GATHERING_WRITE_ATTEMPTED_LOW_THRESHOLD; @@ -461,7 +463,6 @@ protected Executor prepareToClose() { private final class NioSocketChannelConfig extends DefaultSocketChannelConfig { private volatile int maxBytesPerGatheringWrite = Integer.MAX_VALUE; - private NioSocketChannelConfig(NioSocketChannel channel, Socket javaSocket) { super(channel, javaSocket); calculateMaxBytesPerGatheringWrite(); @@ -479,6 +480,31 @@ public NioSocketChannelConfig setSendBufferSize(int sendBufferSize) { return this; } + @Override + public boolean setOption(ChannelOption option, T value) { + if (PlatformDependent.javaVersion() >= 7 && option instanceof NioChannelOption) { + return NioChannelOption.setOption(jdkChannel(), (NioChannelOption) option, value); + } + return super.setOption(option, value); + } + + @Override + public T getOption(ChannelOption option) { + if (PlatformDependent.javaVersion() >= 7 && option instanceof NioChannelOption) { + return NioChannelOption.getOption(jdkChannel(), (NioChannelOption) option); + } + return super.getOption(option); + } + + @SuppressWarnings("unchecked") + @Override + public Map, Object> getOptions() { + if (PlatformDependent.javaVersion() >= 7) { + return getOptions(super.getOptions(), NioChannelOption.getOptions(jdkChannel())); + } + return super.getOptions(); + } + void setMaxBytesPerGatheringWrite(int maxBytesPerGatheringWrite) { this.maxBytesPerGatheringWrite = maxBytesPerGatheringWrite; } @@ -494,5 +520,9 @@ private void calculateMaxBytesPerGatheringWrite() { setMaxBytesPerGatheringWrite(getSendBufferSize() << 1); } } + + private SocketChannel jdkChannel() { + return ((NioSocketChannel) channel).javaChannel(); + } } } diff --git a/transport/src/test/java/io/netty/channel/socket/nio/AbstractNioChannelTest.java b/transport/src/test/java/io/netty/channel/socket/nio/AbstractNioChannelTest.java new file mode 100644 index 000000000000..c2a8c43dcc2a --- /dev/null +++ b/transport/src/test/java/io/netty/channel/socket/nio/AbstractNioChannelTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.socket.nio; + +import io.netty.channel.ChannelOption; +import io.netty.channel.nio.AbstractNioChannel; +import org.junit.Test; + +import java.io.IOException; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.channels.NetworkChannel; + +import static org.junit.Assert.*; + +public abstract class AbstractNioChannelTest { + + protected abstract T newNioChannel(); + + protected abstract NetworkChannel jdkChannel(T channel); + + protected abstract SocketOption newInvalidOption(); + + @Test + public void testNioChannelOption() throws IOException { + T channel = newNioChannel(); + try { + NetworkChannel jdkChannel = jdkChannel(channel); + ChannelOption option = NioChannelOption.of(StandardSocketOptions.SO_REUSEADDR); + boolean value1 = jdkChannel.getOption(StandardSocketOptions.SO_REUSEADDR); + boolean value2 = channel.config().getOption(option); + + assertEquals(value1, value2); + + channel.config().setOption(option, !value2); + boolean value3 = jdkChannel.getOption(StandardSocketOptions.SO_REUSEADDR); + boolean value4 = channel.config().getOption(option); + assertEquals(value3, value4); + assertNotEquals(value1, value4); + } finally { + channel.unsafe().closeForcibly(); + } + } + + @Test + public void testInvalidNioChannelOption() { + T channel = newNioChannel(); + try { + ChannelOption option = NioChannelOption.of(newInvalidOption()); + assertFalse(channel.config().setOption(option, null)); + assertNull(channel.config().getOption(option)); + } finally { + channel.unsafe().closeForcibly(); + } + } +} diff --git a/transport/src/test/java/io/netty/channel/nio/NioDatagramChannelTest.java b/transport/src/test/java/io/netty/channel/socket/nio/NioDatagramChannelTest.java similarity index 78% rename from transport/src/test/java/io/netty/channel/nio/NioDatagramChannelTest.java rename to transport/src/test/java/io/netty/channel/socket/nio/NioDatagramChannelTest.java index 343bd1920e98..d0e235efcbfb 100644 --- a/transport/src/test/java/io/netty/channel/nio/NioDatagramChannelTest.java +++ b/transport/src/test/java/io/netty/channel/socket/nio/NioDatagramChannelTest.java @@ -13,24 +13,27 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.channel.nio; +package io.netty.channel.socket.nio; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelOption; import io.netty.channel.group.DefaultChannelGroup; +import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramChannel; -import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.GlobalEventExecutor; import org.junit.Assert; import org.junit.Test; import java.net.InetSocketAddress; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.channels.NetworkChannel; -public class NioDatagramChannelTest { +public class NioDatagramChannelTest extends AbstractNioChannelTest { /** * Test try to reproduce issue #1335 @@ -61,4 +64,19 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { group.shutdownGracefully().sync(); } } + + @Override + protected NioDatagramChannel newNioChannel() { + return new NioDatagramChannel(); + } + + @Override + protected NetworkChannel jdkChannel(NioDatagramChannel channel) { + return channel.javaChannel(); + } + + @Override + protected SocketOption newInvalidOption() { + return StandardSocketOptions.TCP_NODELAY; + } } diff --git a/transport/src/test/java/io/netty/channel/socket/nio/NioServerSocketChannelTest.java b/transport/src/test/java/io/netty/channel/socket/nio/NioServerSocketChannelTest.java index 80779902f76b..ec502239771f 100644 --- a/transport/src/test/java/io/netty/channel/socket/nio/NioServerSocketChannelTest.java +++ b/transport/src/test/java/io/netty/channel/socket/nio/NioServerSocketChannelTest.java @@ -22,9 +22,12 @@ import java.io.IOException; import java.net.InetSocketAddress; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.channels.NetworkChannel; import java.nio.channels.ServerSocketChannel; -public class NioServerSocketChannelTest { +public class NioServerSocketChannelTest extends AbstractNioChannelTest { @Test public void testCloseOnError() throws Exception { @@ -41,4 +44,19 @@ public void testCloseOnError() throws Exception { group.shutdownGracefully(); } } + + @Override + protected NioServerSocketChannel newNioChannel() { + return new NioServerSocketChannel(); + } + + @Override + protected NetworkChannel jdkChannel(NioServerSocketChannel channel) { + return channel.javaChannel(); + } + + @Override + protected SocketOption newInvalidOption() { + return StandardSocketOptions.IP_MULTICAST_IF; + } } diff --git a/transport/src/test/java/io/netty/channel/nio/NioSocketChannelTest.java b/transport/src/test/java/io/netty/channel/socket/nio/NioSocketChannelTest.java similarity index 94% rename from transport/src/test/java/io/netty/channel/nio/NioSocketChannelTest.java rename to transport/src/test/java/io/netty/channel/socket/nio/NioSocketChannelTest.java index 69b601a7578d..4819c44b04a7 100644 --- a/transport/src/test/java/io/netty/channel/nio/NioSocketChannelTest.java +++ b/transport/src/test/java/io/netty/channel/socket/nio/NioSocketChannelTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.channel.nio; +package io.netty.channel.socket.nio; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; @@ -30,9 +30,8 @@ import io.netty.channel.EventLoop; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; import io.netty.util.internal.PlatformDependent; @@ -46,7 +45,10 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardSocketOptions; import java.nio.channels.ClosedChannelException; +import java.nio.channels.NetworkChannel; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; @@ -55,7 +57,7 @@ import static org.junit.Assert.*; -public class NioSocketChannelTest { +public class NioSocketChannelTest extends AbstractNioChannelTest { /** * Reproduces the issue #1600 @@ -278,4 +280,19 @@ public void testShutdownOutputAndClose() throws IOException { group.shutdownGracefully(); } } + + @Override + protected NioSocketChannel newNioChannel() { + return new NioSocketChannel(); + } + + @Override + protected NetworkChannel jdkChannel(NioSocketChannel channel) { + return channel.javaChannel(); + } + + @Override + protected SocketOption newInvalidOption() { + return StandardSocketOptions.IP_MULTICAST_IF; + } } From 0dc71cee3a50fbcae534c815d6c5a5cd57d81845 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 27 Jul 2018 01:55:21 +0800 Subject: [PATCH 101/417] DefaultPromise.getNow() does not correctly handle DefaultPromise.setUncancellable() (#8154) Motivation: We do not correctly check for previous calles of setUncancellable() in getNow() which may result in ClassCastException as we incorrectly return the internally UNCANCELLABLE object and not null if setUncancellable() we as called before. Modifications: Correctly check for UNCANCELLABLE and add unit test. Result: Fixes https://github.com/netty/netty/issues/8135. --- .../netty/util/concurrent/DefaultPromise.java | 2 +- .../util/concurrent/DefaultPromiseTest.java | 21 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java b/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java index ca67ccfbaaae..a910e408c11c 100644 --- a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java +++ b/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java @@ -301,7 +301,7 @@ public boolean awaitUninterruptibly(long timeoutMillis) { @Override public V getNow() { Object result = this.result; - if (result instanceof CauseHolder || result == SUCCESS) { + if (result instanceof CauseHolder || result == SUCCESS || result == UNCANCELLABLE) { return null; } return (V) result; diff --git a/common/src/test/java/io/netty/util/concurrent/DefaultPromiseTest.java b/common/src/test/java/io/netty/util/concurrent/DefaultPromiseTest.java index 9c29f94a555b..fd9a3e95d695 100644 --- a/common/src/test/java/io/netty/util/concurrent/DefaultPromiseTest.java +++ b/common/src/test/java/io/netty/util/concurrent/DefaultPromiseTest.java @@ -37,10 +37,7 @@ import static java.lang.Math.max; import static org.hamcrest.Matchers.lessThan; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; @SuppressWarnings("unchecked") public class DefaultPromiseTest { @@ -258,6 +255,22 @@ public void signalSuccessCompletionValue() { assertTrue(promise.isSuccess()); } + @Test + public void setUncancellableGetNow() { + final Promise promise = new DefaultPromise(ImmediateEventExecutor.INSTANCE); + assertNull(promise.getNow()); + assertTrue(promise.setUncancellable()); + assertNull(promise.getNow()); + assertFalse(promise.isDone()); + assertFalse(promise.isSuccess()); + + promise.setSuccess("success"); + + assertTrue(promise.isDone()); + assertTrue(promise.isSuccess()); + assertEquals("success", promise.getNow()); + } + private static void testStackOverFlowChainedFuturesA(int promiseChainLength, final EventExecutor executor, boolean runTestInExecutorThread) throws InterruptedException { From 9b08dbca00ffdcb37ec0be2100d868c7c2dd72b9 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 27 Jul 2018 01:56:09 +0800 Subject: [PATCH 102/417] Leak detection combined with composite buffers results in incorrectly handled writerIndex when calling ByteBufUtil.writeAscii/writeUtf8 (#8153) Motivation: We need to add special handling for WrappedCompositeByteBuf as these also extend AbstractByteBuf, otherwise we will not correctly adjust / read the writerIndex during processing. Modifications: - Add instanceof checks for WrappedCompositeByteBuf as well. - Add testcases Result: Fixes https://github.com/netty/netty/issues/8152. --- .../java/io/netty/buffer/ByteBufUtil.java | 10 ++- .../java/io/netty/buffer/ByteBufUtilTest.java | 72 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index 0d1b3a9b89fe..e7afab36728f 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -498,7 +498,10 @@ public static int writeUtf8(ByteBuf buf, CharSequence seq) { */ public static int reserveAndWriteUtf8(ByteBuf buf, CharSequence seq, int reserveBytes) { for (;;) { - if (buf instanceof AbstractByteBuf) { + if (buf instanceof WrappedCompositeByteBuf) { + // WrappedCompositeByteBuf is a sub-class of AbstractByteBuf so it needs special handling. + buf = buf.unwrap(); + } else if (buf instanceof AbstractByteBuf) { AbstractByteBuf byteBuf = (AbstractByteBuf) buf; byteBuf.ensureWritable0(reserveBytes); int written = writeUtf8(byteBuf, byteBuf.writerIndex, seq, seq.length()); @@ -665,7 +668,10 @@ public static int writeAscii(ByteBuf buf, CharSequence seq) { buf.writeBytes(asciiString.array(), asciiString.arrayOffset(), len); } else { for (;;) { - if (buf instanceof AbstractByteBuf) { + if (buf instanceof WrappedCompositeByteBuf) { + // WrappedCompositeByteBuf is a sub-class of AbstractByteBuf so it needs special handling. + buf = buf.unwrap(); + } else if (buf instanceof AbstractByteBuf) { AbstractByteBuf byteBuf = (AbstractByteBuf) buf; byteBuf.ensureWritable0(len); int written = writeAscii(byteBuf, byteBuf.writerIndex, seq, len); diff --git a/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java b/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java index 38e36e20db38..da9b344f406d 100644 --- a/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java +++ b/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java @@ -238,6 +238,42 @@ public void testWriteUsAsciiWrapped() { buf2.unwrap().release(); } + @Test + public void testWriteUsAsciiComposite() { + String usAscii = "NettyRocks"; + ByteBuf buf = Unpooled.buffer(16); + buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); + ByteBuf buf2 = Unpooled.compositeBuffer().addComponent( + Unpooled.buffer(8)).addComponent(Unpooled.buffer(24)); + // write some byte so we start writing with an offset. + buf2.writeByte(1); + ByteBufUtil.writeAscii(buf2, usAscii); + + // Skip the previously written byte. + assertEquals(buf, buf2.skipBytes(1)); + + buf.release(); + buf2.release(); + } + + @Test + public void testWriteUsAsciiCompositeWrapped() { + String usAscii = "NettyRocks"; + ByteBuf buf = Unpooled.buffer(16); + buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); + ByteBuf buf2 = new WrappedCompositeByteBuf(Unpooled.compositeBuffer().addComponent( + Unpooled.buffer(8)).addComponent(Unpooled.buffer(24))); + // write some byte so we start writing with an offset. + buf2.writeByte(1); + ByteBufUtil.writeAscii(buf2, usAscii); + + // Skip the previously written byte. + assertEquals(buf, buf2.skipBytes(1)); + + buf.release(); + buf2.release(); + } + @Test public void testWriteUtf8() { String usAscii = "Some UTF-8 like äÄ∏ŒŒ"; @@ -252,6 +288,42 @@ public void testWriteUtf8() { buf2.release(); } + @Test + public void testWriteUtf8Composite() { + String utf8 = "Some UTF-8 like äÄ∏ŒŒ"; + ByteBuf buf = Unpooled.buffer(16); + buf.writeBytes(utf8.getBytes(CharsetUtil.UTF_8)); + ByteBuf buf2 = Unpooled.compositeBuffer().addComponent( + Unpooled.buffer(8)).addComponent(Unpooled.buffer(24)); + // write some byte so we start writing with an offset. + buf2.writeByte(1); + ByteBufUtil.writeUtf8(buf2, utf8); + + // Skip the previously written byte. + assertEquals(buf, buf2.skipBytes(1)); + + buf.release(); + buf2.release(); + } + + @Test + public void testWriteUtf8CompositeWrapped() { + String utf8 = "Some UTF-8 like äÄ∏ŒŒ"; + ByteBuf buf = Unpooled.buffer(16); + buf.writeBytes(utf8.getBytes(CharsetUtil.UTF_8)); + ByteBuf buf2 = new WrappedCompositeByteBuf(Unpooled.compositeBuffer().addComponent( + Unpooled.buffer(8)).addComponent(Unpooled.buffer(24))); + // write some byte so we start writing with an offset. + buf2.writeByte(1); + ByteBufUtil.writeUtf8(buf2, utf8); + + // Skip the previously written byte. + assertEquals(buf, buf2.skipBytes(1)); + + buf.release(); + buf2.release(); + } + @Test public void testWriteUtf8Surrogates() { // leading surrogate + trailing surrogate From 620dad0c2602440f2c457a5cb2a4c7a29e786bda Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 27 Jul 2018 01:56:32 +0800 Subject: [PATCH 103/417] Allow to validate sni hostname with underscore (#8150) Motivation: We should allow to also validate sni hostname which contains for example underscore when using our native SSL impl. The JDK implementation does this as well. Modifications: - Construct the SNIHostName via byte[] and not String. - Add unit test Result: Fixes https://github.com/netty/netty/issues/8144. --- .../io/netty/handler/ssl/Java8SslUtils.java | 2 +- .../ssl/ReferenceCountedOpenSslEngine.java | 3 ++- .../ReferenceCountedOpenSslServerContext.java | 4 ++- .../netty/handler/ssl/Java8SslTestUtils.java | 5 ++-- .../netty/handler/ssl/OpenSslEngineTest.java | 26 ++++++++++++++++++- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java b/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java index 583d4cf49867..b40c121748d1 100644 --- a/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java +++ b/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java @@ -69,7 +69,7 @@ static void setSNIMatchers(SSLParameters sslParameters, Collection matchers) } @SuppressWarnings("unchecked") - static boolean checkSniHostnameMatch(Collection matchers, String hostname) { + static boolean checkSniHostnameMatch(Collection matchers, byte[] hostname) { if (matchers != null && !matchers.isEmpty()) { SNIHostName name = new SNIHostName(hostname); Iterator matcherIt = (Iterator) matchers.iterator(); diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index b98e9858d7be..8b1473576675 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -20,6 +20,7 @@ import io.netty.internal.tcnative.Buffer; import io.netty.internal.tcnative.SSL; import io.netty.util.AbstractReferenceCounted; +import io.netty.util.CharsetUtil; import io.netty.util.ReferenceCounted; import io.netty.util.ResourceLeakDetector; import io.netty.util.ResourceLeakDetectorFactory; @@ -1817,7 +1818,7 @@ private boolean isDestroyed() { return destroyed != 0; } - final boolean checkSniHostnameMatch(String hostname) { + final boolean checkSniHostnameMatch(byte[] hostname) { return Java8SslUtils.checkSniHostnameMatch(matchers, hostname); } diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java index 9f09393f01e3..b9c8cf60b8ce 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java @@ -19,6 +19,7 @@ import io.netty.internal.tcnative.SSL; import io.netty.internal.tcnative.SSLContext; import io.netty.internal.tcnative.SniHostNameMatcher; +import io.netty.util.CharsetUtil; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -244,7 +245,8 @@ private static final class OpenSslSniHostnameMatcher implements SniHostNameMatch public boolean match(long ssl, String hostname) { ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); if (engine != null) { - return engine.checkSniHostnameMatch(hostname); + // TODO: In the next release of tcnative we should pass the byte[] directly in and not use a String. + return engine.checkSniHostnameMatch(hostname.getBytes(CharsetUtil.UTF_8)); } logger.warn("No ReferenceCountedOpenSslEngine found for SSL pointer: {}", ssl); return false; diff --git a/handler/src/test/java/io/netty/handler/ssl/Java8SslTestUtils.java b/handler/src/test/java/io/netty/handler/ssl/Java8SslTestUtils.java index 32219ff155a7..50fb935d9dc7 100644 --- a/handler/src/test/java/io/netty/handler/ssl/Java8SslTestUtils.java +++ b/handler/src/test/java/io/netty/handler/ssl/Java8SslTestUtils.java @@ -23,17 +23,18 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import java.security.Provider; +import java.util.Arrays; import java.util.Collections; final class Java8SslTestUtils { private Java8SslTestUtils() { } - static void setSNIMatcher(SSLParameters parameters) { + static void setSNIMatcher(SSLParameters parameters, final byte[] match) { SNIMatcher matcher = new SNIMatcher(0) { @Override public boolean matches(SNIServerName sniServerName) { - return false; + return Arrays.equals(match, sniServerName.getEncoded()); } }; parameters.setSNIMatchers(Collections.singleton(matcher)); diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index 9be57e546802..99fb8fa82e23 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -22,6 +22,8 @@ import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.internal.tcnative.SSL; +import io.netty.util.CharsetUtil; +import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.PlatformDependent; import org.junit.Assume; import org.junit.BeforeClass; @@ -986,7 +988,7 @@ public void testSNIMatchersDoesNotThrow() throws Exception { SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { SSLParameters parameters = new SSLParameters(); - Java8SslTestUtils.setSNIMatcher(parameters); + Java8SslTestUtils.setSNIMatcher(parameters, EmptyArrays.EMPTY_BYTES); engine.setSSLParameters(parameters); } finally { cleanupServerSslEngine(engine); @@ -994,6 +996,28 @@ public void testSNIMatchersDoesNotThrow() throws Exception { } } + @Test + public void testSNIMatchersWithSNINameWithUnderscore() throws Exception { + assumeTrue(PlatformDependent.javaVersion() >= 8); + byte[] name = "rb8hx3pww30y3tvw0mwy.v1_1".getBytes(CharsetUtil.UTF_8); + SelfSignedCertificate ssc = new SelfSignedCertificate(); + serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) + .sslProvider(sslServerProvider()) + .build(); + + SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + try { + SSLParameters parameters = new SSLParameters(); + Java8SslTestUtils.setSNIMatcher(parameters, name); + engine.setSSLParameters(parameters); + assertTrue(unwrapEngine(engine).checkSniHostnameMatch(name)); + assertFalse(unwrapEngine(engine).checkSniHostnameMatch("other".getBytes(CharsetUtil.UTF_8))); + } finally { + cleanupServerSslEngine(engine); + ssc.delete(); + } + } + @Test(expected = IllegalArgumentException.class) public void testAlgorithmConstraintsThrows() throws Exception { SelfSignedCertificate ssc = new SelfSignedCertificate(); From 53b2dea3f48a4f24606877b9bb12b04592fe2794 Mon Sep 17 00:00:00 2001 From: Scott Mitchell <7562868+Scottmitch@users.noreply.github.com> Date: Thu, 26 Jul 2018 19:44:21 -0400 Subject: [PATCH 104/417] HTTP/2 child channel read cycle doesn't respect RecvByteBufAllocator and (#8147) Motivation: Http2MultiplexCodec queues data internally if data is delivered from the parent channel but the child channel did not request data. If the parent channel notifies of a stream closure it is possible data in the queue will be discarded before closing the channel. Http2MultiplexCodec interacts with RecvByteBufAllocator to control the child channel's demand for read. However it currently only ever reads a maximum of one time per loop. This can thrash the read loop and bloat the call stack if auto read is on, because channelReadComplete will re-enter the read loop synchronously, and also neglect to deliver data during the parent's read loop (if it is active). This also meant the readPendingQueue was not utilized as originally intended (to extend the child channel's read loop during the parent channel's read loop if demand for data still existed). Modifications: - Modify the child channel's read loop to respect the RecvByteBufAllocator, and append to the parents readPendingQueue if appropriate. - Stream closure notification behaves like EPOLL and KQUEUE transports and reads all queued data, because the data is already queued in memory and it is known there will be no more data. This will also replenish the connection flow control window which may otherwise be constrained by a closed stream. Result: More correct read loop and less risk of dropping data. --- .../codec/http2/Http2MultiplexCodec.java | 326 +++++++++--------- .../codec/http2/Http2MultiplexCodecTest.java | 202 ++++++++++- .../codec/http2/LastInboundHandler.java | 31 ++ .../codec/http2/TestChannelInitializer.java | 84 +++++ 4 files changed, 466 insertions(+), 177 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java index 9c3874c4d3a1..a7ca0bb83114 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java @@ -31,10 +31,10 @@ import io.netty.channel.ChannelPromise; import io.netty.channel.DefaultChannelConfig; import io.netty.channel.DefaultChannelPipeline; -import io.netty.channel.DefaultMaxMessagesRecvByteBufAllocator; import io.netty.channel.EventLoop; import io.netty.channel.MessageSizeEstimator; import io.netty.channel.RecvByteBufAllocator; +import io.netty.channel.RecvByteBufAllocator.Handle; import io.netty.channel.VoidChannelPromise; import io.netty.channel.WriteBufferWaterMark; import io.netty.util.DefaultAttributeMap; @@ -56,7 +56,6 @@ import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid; import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; import static io.netty.handler.codec.http2.Http2Exception.connectionError; - import static java.lang.Math.min; /** @@ -111,7 +110,7 @@ public class Http2MultiplexCodec extends Http2FrameCodec { private static final ChannelFutureListener CHILD_CHANNEL_REGISTRATION_LISTENER = new ChannelFutureListener() { @Override - public void operationComplete(ChannelFuture future) throws Exception { + public void operationComplete(ChannelFuture future) { registerDone(future); } }; @@ -148,19 +147,6 @@ public Handle newHandle() { } } - private static final class Http2StreamChannelRecvByteBufAllocator extends DefaultMaxMessagesRecvByteBufAllocator { - - @Override - public MaxMessageHandle newHandle() { - return new MaxMessageHandle() { - @Override - public int guess() { - return 1024; - } - }; - } - } - private final ChannelHandler inboundStreamHandler; private final ChannelHandler upgradeStreamHandler; @@ -230,7 +216,7 @@ public final void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { while (ch != null) { DefaultHttp2StreamChannel curr = ch; ch = curr.next; - curr.next = null; + curr.next = curr.previous = null; } head = tail = null; } @@ -244,7 +230,7 @@ Http2MultiplexCodecStream newStream() { final void onHttp2Frame(ChannelHandlerContext ctx, Http2Frame frame) { if (frame instanceof Http2StreamFrame) { Http2StreamFrame streamFrame = (Http2StreamFrame) frame; - onHttp2StreamFrame(((Http2MultiplexCodecStream) streamFrame.stream()).channel, streamFrame); + ((Http2MultiplexCodecStream) streamFrame.stream()).channel.fireChildRead(streamFrame); } else if (frame instanceof Http2GoAwayFrame) { onHttp2GoAwayFrame(ctx, (Http2GoAwayFrame) frame); // Allow other handlers to act on GOAWAY frame @@ -331,38 +317,48 @@ final void onHttp2FrameStreamException(ChannelHandlerContext ctx, Http2FrameStre } } - private void onHttp2StreamFrame(DefaultHttp2StreamChannel childChannel, Http2StreamFrame frame) { - switch (childChannel.fireChildRead(frame)) { - case READ_PROCESSED_BUT_STOP_READING: - childChannel.fireChildReadComplete(); - break; - case READ_PROCESSED_OK_TO_PROCESS_MORE: - addChildChannelToReadPendingQueue(childChannel); - break; - case READ_IGNORED_CHANNEL_INACTIVE: - case READ_QUEUED: - // nothing to do: - break; - default: - throw new Error(); + private boolean isChildChannelInReadPendingQueue(DefaultHttp2StreamChannel childChannel) { + return childChannel.previous != null || childChannel.next != null || head == childChannel; + } + + final void tryAddChildChannelToReadPendingQueue(DefaultHttp2StreamChannel childChannel) { + if (!isChildChannelInReadPendingQueue(childChannel)) { + addChildChannelToReadPendingQueue(childChannel); } } final void addChildChannelToReadPendingQueue(DefaultHttp2StreamChannel childChannel) { - if (!childChannel.fireChannelReadPending) { - assert childChannel.next == null; + if (tail == null) { + assert head == null; + tail = head = childChannel; + } else { + childChannel.previous = tail; + tail.next = childChannel; + tail = childChannel; + } + } - if (tail == null) { - assert head == null; - tail = head = childChannel; - } else { - tail.next = childChannel; - tail = childChannel; - } - childChannel.fireChannelReadPending = true; + private void tryRemoveChildChannelFromReadPendingQueue(DefaultHttp2StreamChannel childChannel) { + if (isChildChannelInReadPendingQueue(childChannel)) { + removeChildChannelFromReadPendingQueue(childChannel); } } + private void removeChildChannelFromReadPendingQueue(DefaultHttp2StreamChannel childChannel) { + DefaultHttp2StreamChannel previous = childChannel.previous; + if (childChannel.next != null) { + childChannel.next.previous = previous; + } else { + tail = tail.previous; // If there is no next, this childChannel is the tail, so move the tail back. + } + if (previous != null) { + previous.next = childChannel.next; + } else { + head = head.next; // If there is no previous, this childChannel is the head, so move the tail forward. + } + childChannel.next = childChannel.previous = null; + } + private void onHttp2GoAwayFrame(ChannelHandlerContext ctx, final Http2GoAwayFrame goAwayFrame) { try { forEachActiveStream(new Http2FrameStreamVisitor() { @@ -387,8 +383,14 @@ public boolean visit(Http2FrameStream stream) { */ @Override public final void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - parentReadInProgress = false; - onChannelReadComplete(ctx); + try { + onChannelReadComplete(ctx); + } finally { + parentReadInProgress = false; + tail = head = null; + // We always flush as this is what Http2ConnectionHandler does for now. + flush0(ctx); + } channelReadComplete0(ctx); } @@ -402,23 +404,13 @@ final void onChannelReadComplete(ChannelHandlerContext ctx) { // If we have many child channel we can optimize for the case when multiple call flush() in // channelReadComplete(...) callbacks and only do it once as otherwise we will end-up with multiple // write calls on the socket which is expensive. - try { - DefaultHttp2StreamChannel current = head; - while (current != null) { - DefaultHttp2StreamChannel childChannel = current; - if (childChannel.fireChannelReadPending) { - // Clear early in case fireChildReadComplete() causes it to need to be re-processed - childChannel.fireChannelReadPending = false; - childChannel.fireChildReadComplete(); - } - childChannel.next = null; - current = current.next; - } - } finally { - tail = head = null; - - // We always flush as this is what Http2ConnectionHandler does for now. - flush0(ctx); + DefaultHttp2StreamChannel current = head; + while (current != null) { + DefaultHttp2StreamChannel childChannel = current; + // Clear early in case fireChildReadComplete() causes it to need to be re-processed + current = current.next; + childChannel.next = childChannel.previous = null; + childChannel.fireChildReadComplete(); } } @@ -447,13 +439,6 @@ static class Http2MultiplexCodecStream extends DefaultHttp2FrameStream { DefaultHttp2StreamChannel channel; } - private enum ReadState { - READ_QUEUED, - READ_IGNORED_CHANNEL_INACTIVE, - READ_PROCESSED_BUT_STOP_READING, - READ_PROCESSED_OK_TO_PROCESS_MORE - } - private boolean initialWritability(DefaultHttp2FrameStream stream) { // If the stream id is not valid yet we will just mark the channel as writable as we will be notified // about non-writability state as soon as the first Http2HeaderFrame is written (if needed). @@ -476,24 +461,24 @@ private final class DefaultHttp2StreamChannel extends DefaultAttributeMap implem private volatile boolean writable; private boolean outboundClosed; - private boolean closePending; + /** + * This variable represents if a read is in progress for the current channel. Note that depending upon the + * {@link RecvByteBufAllocator} behavior a read may extend beyond the {@link Http2ChannelUnsafe#beginRead()} + * method scope. The {@link Http2ChannelUnsafe#beginRead()} loop may drain all pending data, and then if the + * parent channel is reading this channel may still accept frames. + */ private boolean readInProgress; private Queue inboundBuffer; /** {@code true} after the first HEADERS frame has been written **/ private boolean firstFrameWritten; - /** {@code true} if a close without an error was initiated **/ - private boolean streamClosedWithoutError; - - // Keeps track of flush calls in channelReadComplete(...) and aggregate these. - private boolean inFireChannelReadComplete; - - boolean fireChannelReadPending; - - // Holds the reference to the next DefaultHttp2StreamChannel that should be processed in - // channelReadComplete(...) + // Currently the child channel and parent channel are always on the same EventLoop thread. This allows us to + // extend the read loop of a child channel if the child channel drains its queued data during read, and the + // parent channel is still in its read loop. The next/previous links build a doubly linked list that the parent + // channel will iterate in its channelReadComplete to end the read cycle for each child channel in the list. DefaultHttp2StreamChannel next; + DefaultHttp2StreamChannel previous; DefaultHttp2StreamChannel(DefaultHttp2FrameStream stream, boolean outbound) { this.stream = stream; @@ -521,13 +506,10 @@ public Http2FrameStream stream() { } void streamClosed() { - streamClosedWithoutError = true; - if (readInProgress) { - // Just call closeForcibly() as this will take care of fireChannelInactive(). - unsafe().closeForcibly(); - } else { - closePending = true; - } + unsafe.readEOS(); + // Attempt to drain any queued data from the queue and deliver it to the application before closing this + // channel. + unsafe.doBeginRead(); } @Override @@ -771,49 +753,48 @@ void writabilityChanged(boolean writable) { * Receive a read message. This does not notify handlers unless a read is in progress on the * channel. */ - ReadState fireChildRead(Http2Frame frame) { + void fireChildRead(Http2Frame frame) { assert eventLoop().inEventLoop(); if (!isActive()) { ReferenceCountUtil.release(frame); - return ReadState.READ_IGNORED_CHANNEL_INACTIVE; - } - if (readInProgress && (inboundBuffer == null || inboundBuffer.isEmpty())) { - // Check for null because inboundBuffer doesn't support null; we want to be consistent - // for what values are supported. - RecvByteBufAllocator.ExtendedHandle allocHandle = unsafe.recvBufAllocHandle(); + } else if (readInProgress) { + // If readInProgress there cannot be anything in the queue, otherwise we would have drained it from the + // queue and processed it during the read cycle. + assert inboundBuffer == null || inboundBuffer.isEmpty(); + final Handle allocHandle = unsafe.recvBufAllocHandle(); unsafe.doRead0(frame, allocHandle); - return allocHandle.continueReading() ? - ReadState.READ_PROCESSED_OK_TO_PROCESS_MORE : ReadState.READ_PROCESSED_BUT_STOP_READING; + // We currently don't need to check for readEOS because the parent channel and child channel are limited + // to the same EventLoop thread. There are a limited number of frame types that may come after EOS is + // read (unknown, reset) and the trade off is less conditionals for the hot path (headers/data) at the + // cost of additional readComplete notifications on the rare path. + if (allocHandle.continueReading()) { + tryAddChildChannelToReadPendingQueue(this); + } else { + tryRemoveChildChannelFromReadPendingQueue(this); + unsafe.notifyReadComplete(allocHandle); + } } else { if (inboundBuffer == null) { inboundBuffer = new ArrayDeque(4); } inboundBuffer.add(frame); - return ReadState.READ_QUEUED; } } void fireChildReadComplete() { assert eventLoop().inEventLoop(); - try { - if (readInProgress) { - inFireChannelReadComplete = true; - readInProgress = false; - unsafe().recvBufAllocHandle().readComplete(); - pipeline().fireChannelReadComplete(); - } - } finally { - inFireChannelReadComplete = false; - } + assert readInProgress; + unsafe.notifyReadComplete(unsafe.recvBufAllocHandle()); } private final class Http2ChannelUnsafe implements Unsafe { private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(DefaultHttp2StreamChannel.this, false); @SuppressWarnings("deprecation") - private RecvByteBufAllocator.ExtendedHandle recvHandle; + private Handle recvHandle; private boolean writeDoneAndNoFlush; private boolean closeInitiated; + private boolean readEOS; @Override public void connect(final SocketAddress remoteAddress, @@ -825,9 +806,10 @@ public void connect(final SocketAddress remoteAddress, } @Override - public RecvByteBufAllocator.ExtendedHandle recvBufAllocHandle() { + public Handle recvBufAllocHandle() { if (recvHandle == null) { - recvHandle = (RecvByteBufAllocator.ExtendedHandle) config().getRecvByteBufAllocator().newHandle(); + recvHandle = config().getRecvByteBufAllocator().newHandle(); + recvHandle.reset(config()); } return recvHandle; } @@ -892,7 +874,7 @@ public void close(final ChannelPromise promise) { // This means close() was called before so we just register a listener and return closePromise.addListener(new ChannelFutureListener() { @Override - public void operationComplete(ChannelFuture future) throws Exception { + public void operationComplete(ChannelFuture future) { promise.setSuccess(); } }); @@ -901,15 +883,13 @@ public void operationComplete(ChannelFuture future) throws Exception { } closeInitiated = true; - closePending = false; - fireChannelReadPending = false; + tryRemoveChildChannelFromReadPendingQueue(DefaultHttp2StreamChannel.this); final boolean wasActive = isActive(); // Only ever send a reset frame if the connection is still alive and if the stream may have existed // as otherwise we may send a RST on a stream in an invalid state and cause a connection error. - if (parent().isActive() && !streamClosedWithoutError && - connection().streamMayHaveExisted(stream().id())) { + if (parent().isActive() && !readEOS && connection().streamMayHaveExisted(stream().id())) { Http2StreamFrame resetFrame = new DefaultHttp2ResetFrame(Http2Error.CANCEL).stream(stream()); write(resetFrame, unsafe().voidPromise()); flush(); @@ -1009,65 +989,75 @@ public void beginRead() { return; } readInProgress = true; + doBeginRead(); + } - final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle(); - allocHandle.reset(config()); - if (inboundBuffer == null || inboundBuffer.isEmpty()) { - if (closePending) { + void doBeginRead() { + Object message; + if (inboundBuffer == null || (message = inboundBuffer.poll()) == null) { + if (readEOS) { unsafe.closeForcibly(); } - return; + } else { + final Handle allocHandle = recvBufAllocHandle(); + allocHandle.reset(config()); + boolean continueReading = false; + do { + doRead0((Http2Frame) message, allocHandle); + } while ((readEOS || (continueReading = allocHandle.continueReading())) && + (message = inboundBuffer.poll()) != null); + + if (continueReading && parentReadInProgress && !readEOS) { + // Currently the parent and child channel are on the same EventLoop thread. If the parent is + // currently reading it is possile that more frames will be delivered to this child channel. In + // the case that this child channel still wants to read we delay the channelReadComplete on this + // child channel until the parent is done reading. + assert !isChildChannelInReadPendingQueue(DefaultHttp2StreamChannel.this); + addChildChannelToReadPendingQueue(DefaultHttp2StreamChannel.this); + } else { + notifyReadComplete(allocHandle); + } } + } - // We have already checked that the queue is not empty, so before this value is used it will always be - // set by allocHandle.continueReading(). - boolean continueReading; - do { - Object m = inboundBuffer.poll(); - if (m == null) { - continueReading = false; - break; - } - doRead0((Http2Frame) m, allocHandle); - } while (continueReading = allocHandle.continueReading()); + void readEOS() { + readEOS = true; + } - if (continueReading && parentReadInProgress) { - // We don't know if more frames will be delivered in the parent channel's read loop, so add this - // channel to the channelReadComplete queue to be notified later. - addChildChannelToReadPendingQueue(DefaultHttp2StreamChannel.this); - } else { - // Reading data may result in frames being written (e.g. WINDOW_UPDATE, RST, etc..). If the parent - // channel is not currently reading we need to force a flush at the child channel, because we cannot - // rely upon flush occurring in channelReadComplete on the parent channel. - readInProgress = false; - allocHandle.readComplete(); - pipeline().fireChannelReadComplete(); - flush(); - if (closePending) { - unsafe.closeForcibly(); - } + void notifyReadComplete(Handle allocHandle) { + assert next == null && previous == null; + readInProgress = false; + allocHandle.readComplete(); + pipeline().fireChannelReadComplete(); + // Reading data may result in frames being written (e.g. WINDOW_UPDATE, RST, etc..). If the parent + // channel is not currently reading we need to force a flush at the child channel, because we cannot + // rely upon flush occurring in channelReadComplete on the parent channel. + flush(); + if (readEOS) { + unsafe.closeForcibly(); } } @SuppressWarnings("deprecation") - void doRead0(Http2Frame frame, RecvByteBufAllocator.Handle allocHandle) { - int numBytesToBeConsumed = 0; + void doRead0(Http2Frame frame, Handle allocHandle) { + pipeline().fireChannelRead(frame); + allocHandle.incMessagesRead(1); + if (frame instanceof Http2DataFrame) { - numBytesToBeConsumed = ((Http2DataFrame) frame).initialFlowControlledBytes(); + final int numBytesToBeConsumed = ((Http2DataFrame) frame).initialFlowControlledBytes(); + allocHandle.attemptedBytesRead(numBytesToBeConsumed); allocHandle.lastBytesRead(numBytesToBeConsumed); + if (numBytesToBeConsumed != 0) { + try { + writeDoneAndNoFlush |= onBytesConsumed(ctx, stream, numBytesToBeConsumed); + } catch (Http2Exception e) { + pipeline().fireExceptionCaught(e); + } + } } else { + allocHandle.attemptedBytesRead(MIN_HTTP2_FRAME_SIZE); allocHandle.lastBytesRead(MIN_HTTP2_FRAME_SIZE); } - allocHandle.incMessagesRead(1); - pipeline().fireChannelRead(frame); - - if (numBytesToBeConsumed != 0) { - try { - writeDoneAndNoFlush |= onBytesConsumed(ctx, stream, numBytesToBeConsumed); - } catch (Http2Exception e) { - pipeline().fireExceptionCaught(e); - } - } } @Override @@ -1104,7 +1094,7 @@ public void write(Object msg, final ChannelPromise promise) { } else { future.addListener(new ChannelFutureListener() { @Override - public void operationComplete(ChannelFuture future) throws Exception { + public void operationComplete(ChannelFuture future) { firstWriteComplete(future, promise); } }); @@ -1126,7 +1116,7 @@ public void operationComplete(ChannelFuture future) throws Exception { } else { future.addListener(new ChannelFutureListener() { @Override - public void operationComplete(ChannelFuture future) throws Exception { + public void operationComplete(ChannelFuture future) { writeComplete(future, promise); } }); @@ -1197,18 +1187,16 @@ private ChannelFuture write0(Object msg) { @Override public void flush() { - if (!writeDoneAndNoFlush) { + // If we are currently in the parent channel's read loop we should just ignore the flush. + // We will ensure we trigger ctx.flush() after we processed all Channels later on and + // so aggregate the flushes. This is done as ctx.flush() is expensive when as it may trigger an + // write(...) or writev(...) operation on the socket. + if (!writeDoneAndNoFlush || parentReadInProgress) { // There is nothing to flush so this is a NOOP. return; } try { - // If we are currently in the channelReadComplete(...) call we should just ignore the flush. - // We will ensure we trigger ctx.flush() after we processed all Channels later on and - // so aggregate the flushes. This is done as ctx.flush() is expensive when as it may trigger an - // write(...) or writev(...) operation on the socket. - if (!inFireChannelReadComplete) { - flush0(ctx); - } + flush0(ctx); } finally { writeDoneAndNoFlush = false; } @@ -1232,10 +1220,8 @@ public ChannelOutboundBuffer outboundBuffer() { * changes. */ private final class Http2StreamChannelConfig extends DefaultChannelConfig { - Http2StreamChannelConfig(Channel channel) { super(channel); - setRecvByteBufAllocator(new Http2StreamChannelRecvByteBufAllocator()); } @Override diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java index f96b12bbe6b5..f09d55e91656 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java @@ -28,8 +28,14 @@ import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpScheme; import io.netty.handler.codec.http2.Http2Exception.StreamException; +import io.netty.handler.codec.http2.LastInboundHandler.Consumer; import io.netty.util.AsciiString; import io.netty.util.AttributeKey; +import io.netty.util.ReferenceCountUtil; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; import java.net.InetSocketAddress; import java.nio.channels.ClosedChannelException; @@ -38,12 +44,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import io.netty.util.ReferenceCountUtil; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - import static io.netty.util.ReferenceCountUtil.release; import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; @@ -743,9 +743,197 @@ public void callUnsafeCloseMultipleTimes() { childChannel.closeFuture().syncUninterruptibly(); } + @Test + public void endOfStreamDoesNotDiscardData() { + AtomicInteger numReads = new AtomicInteger(1); + final AtomicBoolean shouldDisableAutoRead = new AtomicBoolean(); + Consumer ctxConsumer = new Consumer() { + @Override + public void accept(ChannelHandlerContext obj) { + if (shouldDisableAutoRead.get()) { + obj.channel().config().setAutoRead(false); + } + } + }; + LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream, numReads, ctxConsumer); + Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + childChannel.config().setAutoRead(false); + + Http2DataFrame dataFrame1 = new DefaultHttp2DataFrame(bb("1")).stream(inboundStream); + Http2DataFrame dataFrame2 = new DefaultHttp2DataFrame(bb("2")).stream(inboundStream); + Http2DataFrame dataFrame3 = new DefaultHttp2DataFrame(bb("3")).stream(inboundStream); + Http2DataFrame dataFrame4 = new DefaultHttp2DataFrame(bb("4")).stream(inboundStream); + + assertEquals(new DefaultHttp2HeadersFrame(request).stream(inboundStream), inboundHandler.readInbound()); + + // We want to simulate the parent channel calling channelRead and delay calling channelReadComplete. + parentChannel.writeOneInbound(new Object()); + codec.onHttp2Frame(dataFrame1); + assertEquals(dataFrame1, inboundHandler.readInbound()); + + // Deliver frames, and then a stream closed while read is inactive. + codec.onHttp2Frame(dataFrame2); + codec.onHttp2Frame(dataFrame3); + codec.onHttp2Frame(dataFrame4); + + shouldDisableAutoRead.set(true); + childChannel.config().setAutoRead(true); + numReads.set(1); + + inboundStream.state = Http2Stream.State.CLOSED; + codec.onHttp2StreamStateChanged(inboundStream); + + // Detecting EOS should flush all pending data regardless of read calls. + assertEquals(dataFrame2, inboundHandler.readInbound()); + assertEquals(dataFrame3, inboundHandler.readInbound()); + assertEquals(dataFrame4, inboundHandler.readInbound()); + assertNull(inboundHandler.readInbound()); + + // Now we want to call channelReadComplete and simulate the end of the read loop. + parentChannel.flushInbound(); + + childChannel.closeFuture().syncUninterruptibly(); + + dataFrame1.release(); + dataFrame2.release(); + dataFrame3.release(); + dataFrame4.release(); + } + + @Test + public void childQueueIsDrainedAndNewDataIsDispatchedInParentReadLoopAutoRead() { + AtomicInteger numReads = new AtomicInteger(1); + final AtomicInteger channelReadCompleteCount = new AtomicInteger(0); + final AtomicBoolean shouldDisableAutoRead = new AtomicBoolean(); + Consumer ctxConsumer = new Consumer() { + @Override + public void accept(ChannelHandlerContext obj) { + channelReadCompleteCount.incrementAndGet(); + if (shouldDisableAutoRead.get()) { + obj.channel().config().setAutoRead(false); + } + } + }; + LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream, numReads, ctxConsumer); + Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + childChannel.config().setAutoRead(false); + + Http2DataFrame dataFrame1 = new DefaultHttp2DataFrame(bb("1")).stream(inboundStream); + Http2DataFrame dataFrame2 = new DefaultHttp2DataFrame(bb("2")).stream(inboundStream); + Http2DataFrame dataFrame3 = new DefaultHttp2DataFrame(bb("3")).stream(inboundStream); + Http2DataFrame dataFrame4 = new DefaultHttp2DataFrame(bb("4")).stream(inboundStream); + + assertEquals(new DefaultHttp2HeadersFrame(request).stream(inboundStream), inboundHandler.readInbound()); + + // We want to simulate the parent channel calling channelRead and delay calling channelReadComplete. + parentChannel.writeOneInbound(new Object()); + codec.onHttp2Frame(dataFrame1); + assertEquals(dataFrame1, inboundHandler.readInbound()); + + // We want one item to be in the queue, and allow the numReads to be larger than 1. This will ensure that + // when beginRead() is called the child channel is added to the readPending queue of the parent channel. + codec.onHttp2Frame(dataFrame2); + + numReads.set(10); + shouldDisableAutoRead.set(true); + childChannel.config().setAutoRead(true); + + codec.onHttp2Frame(dataFrame3); + codec.onHttp2Frame(dataFrame4); + + // Detecting EOS should flush all pending data regardless of read calls. + assertEquals(dataFrame2, inboundHandler.readInbound()); + assertEquals(dataFrame3, inboundHandler.readInbound()); + assertEquals(dataFrame4, inboundHandler.readInbound()); + assertNull(inboundHandler.readInbound()); + + // Now we want to call channelReadComplete and simulate the end of the read loop. + parentChannel.flushInbound(); + + // 3 = 1 for initialization + 1 for read when auto read was off + 1 for when auto read was back on + assertEquals(3, channelReadCompleteCount.get()); + + dataFrame1.release(); + dataFrame2.release(); + dataFrame3.release(); + dataFrame4.release(); + } + + @Test + public void childQueueIsDrainedAndNewDataIsDispatchedInParentReadLoopNoAutoRead() { + AtomicInteger numReads = new AtomicInteger(1); + final AtomicInteger channelReadCompleteCount = new AtomicInteger(0); + final AtomicBoolean shouldDisableAutoRead = new AtomicBoolean(); + Consumer ctxConsumer = new Consumer() { + @Override + public void accept(ChannelHandlerContext obj) { + channelReadCompleteCount.incrementAndGet(); + if (shouldDisableAutoRead.get()) { + obj.channel().config().setAutoRead(false); + } + } + }; + LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream, numReads, ctxConsumer); + Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + childChannel.config().setAutoRead(false); + + Http2DataFrame dataFrame1 = new DefaultHttp2DataFrame(bb("1")).stream(inboundStream); + Http2DataFrame dataFrame2 = new DefaultHttp2DataFrame(bb("2")).stream(inboundStream); + Http2DataFrame dataFrame3 = new DefaultHttp2DataFrame(bb("3")).stream(inboundStream); + Http2DataFrame dataFrame4 = new DefaultHttp2DataFrame(bb("4")).stream(inboundStream); + + assertEquals(new DefaultHttp2HeadersFrame(request).stream(inboundStream), inboundHandler.readInbound()); + + // We want to simulate the parent channel calling channelRead and delay calling channelReadComplete. + parentChannel.writeOneInbound(new Object()); + codec.onHttp2Frame(dataFrame1); + assertEquals(dataFrame1, inboundHandler.readInbound()); + + // We want one item to be in the queue, and allow the numReads to be larger than 1. This will ensure that + // when beginRead() is called the child channel is added to the readPending queue of the parent channel. + codec.onHttp2Frame(dataFrame2); + + numReads.set(2); + childChannel.read(); + assertEquals(dataFrame2, inboundHandler.readInbound()); + assertNull(inboundHandler.readInbound()); + + // This is the second item that was read, this should be the last until we call read() again. This should also + // notify of readComplete(). + codec.onHttp2Frame(dataFrame3); + assertEquals(dataFrame3, inboundHandler.readInbound()); + + codec.onHttp2Frame(dataFrame4); + assertNull(inboundHandler.readInbound()); + + childChannel.read(); + assertEquals(dataFrame4, inboundHandler.readInbound()); + assertNull(inboundHandler.readInbound()); + + // Now we want to call channelReadComplete and simulate the end of the read loop. + parentChannel.flushInbound(); + + // 3 = 1 for initialization + 1 for first read of 2 items + 1 for second read of 2 items + + // 1 for parent channel readComplete + assertEquals(4, channelReadCompleteCount.get()); + + dataFrame1.release(); + dataFrame2.release(); + dataFrame3.release(); + dataFrame4.release(); + } + private LastInboundHandler streamActiveAndWriteHeaders(Http2FrameStream stream) { - LastInboundHandler inboundHandler = new LastInboundHandler(); + return streamActiveAndWriteHeaders(stream, null, LastInboundHandler.noopConsumer()); + } + + private LastInboundHandler streamActiveAndWriteHeaders(Http2FrameStream stream, + AtomicInteger maxReads, + Consumer contextConsumer) { + + LastInboundHandler inboundHandler = new LastInboundHandler(contextConsumer); childChannelInitializer.handler = inboundHandler; + childChannelInitializer.maxReads = maxReads; assertFalse(inboundHandler.isChannelActive()); ((TestableHttp2MultiplexCodec.Stream) stream).state = Http2Stream.State.OPEN; codec.onHttp2StreamStateChanged(stream); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/LastInboundHandler.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/LastInboundHandler.java index 55c4df0a984a..38f400af14ee 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/LastInboundHandler.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/LastInboundHandler.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.concurrent.locks.LockSupport; +import static io.netty.util.internal.ObjectUtil.checkNotNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; /** @@ -34,11 +35,36 @@ */ public class LastInboundHandler extends ChannelDuplexHandler { private final List queue = new ArrayList(); + private final Consumer channelReadCompleteConsumer; private Throwable lastException; private ChannelHandlerContext ctx; private boolean channelActive; private String writabilityStates = ""; + // TODO(scott): use JDK 8's Consumer + public interface Consumer { + void accept(T obj); + } + + private static final Consumer NOOP_CONSUMER = new Consumer() { + @Override + public void accept(Object obj) { + } + }; + + @SuppressWarnings("unchecked") + public static Consumer noopConsumer() { + return (Consumer) NOOP_CONSUMER; + } + + public LastInboundHandler() { + this(LastInboundHandler.noopConsumer()); + } + + public LastInboundHandler(Consumer channelReadCompleteConsumer) { + this.channelReadCompleteConsumer = checkNotNull(channelReadCompleteConsumer, "channelReadCompleteConsumer"); + } + @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { super.handlerAdded(ctx); @@ -86,6 +112,11 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception queue.add(msg); } + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + channelReadCompleteConsumer.accept(ctx); + } + @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { queue.add(new UserEvent(evt)); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java index 5e13d056bbd6..015550f078d1 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java @@ -16,10 +16,17 @@ package io.netty.handler.codec.http2; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import io.netty.channel.Channel; +import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelInitializer; +import io.netty.channel.RecvByteBufAllocator; +import io.netty.util.UncheckedBooleanSupplier; + +import java.util.concurrent.atomic.AtomicInteger; /** * Channel initializer useful in tests. @@ -27,6 +34,7 @@ @Sharable public class TestChannelInitializer extends ChannelInitializer { ChannelHandler handler; + AtomicInteger maxReads; @Override public void initChannel(Channel channel) { @@ -34,5 +42,81 @@ public void initChannel(Channel channel) { channel.pipeline().addLast(handler); handler = null; } + if (maxReads != null) { + channel.config().setRecvByteBufAllocator(new TestNumReadsRecvByteBufAllocator(maxReads)); + } + } + + /** + * Designed to read a single byte at a time to control the number of reads done at a fine granularity. + */ + private static final class TestNumReadsRecvByteBufAllocator implements RecvByteBufAllocator { + private final AtomicInteger numReads; + TestNumReadsRecvByteBufAllocator(AtomicInteger numReads) { + this.numReads = numReads; + } + + @Override + public ExtendedHandle newHandle() { + return new ExtendedHandle() { + private int attemptedBytesRead; + private int lastBytesRead; + private int numMessagesRead; + @Override + public ByteBuf allocate(ByteBufAllocator alloc) { + return alloc.ioBuffer(guess(), guess()); + } + + @Override + public int guess() { + return 1; // only ever allocate buffers of size 1 to ensure the number of reads is controlled. + } + + @Override + public void reset(ChannelConfig config) { + numMessagesRead = 0; + } + + @Override + public void incMessagesRead(int numMessages) { + numMessagesRead += numMessages; + } + + @Override + public void lastBytesRead(int bytes) { + lastBytesRead = bytes; + } + + @Override + public int lastBytesRead() { + return lastBytesRead; + } + + @Override + public void attemptedBytesRead(int bytes) { + attemptedBytesRead = bytes; + } + + @Override + public int attemptedBytesRead() { + return attemptedBytesRead; + } + + @Override + public boolean continueReading() { + return numMessagesRead < numReads.get(); + } + + @Override + public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) { + return continueReading(); + } + + @Override + public void readComplete() { + // Nothing needs to be done or adjusted after each read cycle is completed. + } + }; + } } } From ff785fbe39152bd78c0bb45dcc882e3ce0af33fc Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Jul 2018 04:59:06 +0000 Subject: [PATCH 105/417] [maven-release-plugin] prepare release netty-4.1.28.Final --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 12 ++-- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 80 insertions(+), 78 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 5c63cf3fc731..752c9f3cd363 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 6e14a4b92adc..d3beacb4702b 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.28.Final-SNAPSHOT + 4.1.28.Final pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.28.Final @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec-dns - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec-haproxy - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec-http - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec-http2 - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec-memcache - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec-mqtt - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec-redis - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec-smtp - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec-socks - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec-stomp - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-codec-xml - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-common - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-dev-tools - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-handler - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-handler-proxy - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-resolver - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-resolver-dns - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-transport - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-transport-rxtx - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-transport-sctp - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-transport-udt - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-example - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-all - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-transport-native-unix-common - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-transport-native-unix-common - 4.1.28.Final-SNAPSHOT + 4.1.28.Final linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.28.Final-SNAPSHOT + 4.1.28.Final osx-x86_64 io.netty netty-transport-native-epoll - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-transport-native-epoll - 4.1.28.Final-SNAPSHOT + 4.1.28.Final linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.28.Final-SNAPSHOT + 4.1.28.Final io.netty netty-transport-native-kqueue - 4.1.28.Final-SNAPSHOT + 4.1.28.Final osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 00e38d31b322..91f62ba79953 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 1e56d2f8e4c0..eba9ea415a1f 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 4830d9ce2dce..c860d494ca16 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 35e7a347778c..012a2b6a23af 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index e3e97a74663d..137dc0005451 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 146dceb2c5ad..d6949ce0a96f 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index d8042bd13513..8678b6712c97 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 8879945b4456..bc41099e2ee7 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index f3186748e5db..cc22f5360883 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index ef4cecc2b61a..e6888cc1f6fd 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 4117dc22f838..72409cba2bb1 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index 425a9ca75c1e..a439ae9cc2a7 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index eec11b9bd24d..ae53a0e37d1e 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-codec diff --git a/common/pom.xml b/common/pom.xml index ba924b10805c..b9ce6852114e 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 4160ad1080c0..437bf59c0400 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.28.Final-SNAPSHOT + 4.1.28.Final Netty/Dev-Tools @@ -50,4 +50,8 @@ + + + netty-4.1.28.Final + diff --git a/example/pom.xml b/example/pom.xml index d9b907df376e..a07b790a7cdf 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 1f589732dfe1..d6c8c0729782 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index 2299f7ba1572..c4043dd8f5dc 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 894ba0d2b943..b64623d210f1 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-microbench diff --git a/pom.xml b/pom.xml index 76412c6b2c33..f98695088536 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.28.Final-SNAPSHOT + 4.1.28.Final Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.28.Final diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index 0448ef8ab840..d59e6d6e3a76 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index 8fcf4c836bb6..9456f50ef338 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 41a7964144b8..e865e8731fbc 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 6708470fbcdc..832411e4155c 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index de7792d4dcff..fc36bc383ea4 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 3680714d4f8a..a926454c70fc 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index b3997bf2f41c..fca0364c2f90 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-testsuite-shading @@ -127,9 +127,8 @@ - - + + @@ -219,9 +218,8 @@ - - + + diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 6f20bfd96b11..add45a8daae8 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 499da0168a69..19d47e4701de 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 1af0f8cf7436..1f8c9f5acc9f 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index eb46ffcdda1e..5b8c4b8b791c 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 4e5032f4961c..a7875b201fcc 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index 46aaae2f39e9..eccfdd5cb650 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 146c45b1b024..e69de60eed09 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 3695dbb20c06..c0f7ddfd9b57 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index c3361fa3f841..01865238fe8e 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final-SNAPSHOT + 4.1.28.Final netty-transport From fcb19cb58944c2db0ed1c76dc7762ca78e819376 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Jul 2018 04:59:28 +0000 Subject: [PATCH 106/417] [maven-release-plugin] prepare for next development iteration --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 72 insertions(+), 76 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 752c9f3cd363..19e371276d6b 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-all diff --git a/bom/pom.xml b/bom/pom.xml index d3beacb4702b..ca3aa4a20bea 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.28.Final + 4.1.29.Final-SNAPSHOT pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.28.Final + HEAD @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec-dns - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec-haproxy - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec-http - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec-http2 - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec-memcache - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec-mqtt - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec-redis - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec-smtp - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec-socks - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec-stomp - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-codec-xml - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-common - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-dev-tools - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-handler - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-handler-proxy - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-resolver - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-resolver-dns - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-transport - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-transport-rxtx - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-transport-sctp - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-transport-udt - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-example - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-all - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.28.Final + 4.1.29.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.28.Final + 4.1.29.Final-SNAPSHOT osx-x86_64 io.netty netty-transport-native-epoll - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-transport-native-epoll - 4.1.28.Final + 4.1.29.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.28.Final + 4.1.29.Final-SNAPSHOT io.netty netty-transport-native-kqueue - 4.1.28.Final + 4.1.29.Final-SNAPSHOT osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 91f62ba79953..90811150e5bb 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index eba9ea415a1f..ff32a88a3f7e 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index c860d494ca16..d82845e30d16 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 012a2b6a23af..4d074f0ae923 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 137dc0005451..984886fa231a 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index d6949ce0a96f..d4485edcb47f 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 8678b6712c97..5dda802bdcc6 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index bc41099e2ee7..78876d583dab 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index cc22f5360883..fb309ceaf035 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index e6888cc1f6fd..856546e31b9c 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 72409cba2bb1..b19fbb8fac06 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index a439ae9cc2a7..a3f4e0540092 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index ae53a0e37d1e..35cd0c3b898d 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-codec diff --git a/common/pom.xml b/common/pom.xml index b9ce6852114e..6292c3653557 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 437bf59c0400..3d985746f3c4 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.28.Final + 4.1.29.Final-SNAPSHOT Netty/Dev-Tools @@ -50,8 +50,4 @@ - - - netty-4.1.28.Final - diff --git a/example/pom.xml b/example/pom.xml index a07b790a7cdf..e4364db219e3 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index d6c8c0729782..c4df9f610222 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index c4043dd8f5dc..25f87786dce0 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index b64623d210f1..536c52b073e5 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-microbench diff --git a/pom.xml b/pom.xml index f98695088536..8b5a4f23fbd9 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.28.Final + 4.1.29.Final-SNAPSHOT Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.28.Final + HEAD diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index d59e6d6e3a76..67963f967668 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index 9456f50ef338..eb369cc96c59 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index e865e8731fbc..e29da6352cb9 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 832411e4155c..c0e9cc35e866 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index fc36bc383ea4..dbe9ccfc9f81 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index a926454c70fc..5b580fdfd51d 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index fca0364c2f90..9e859dc09b13 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index add45a8daae8..30cb691a81c6 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 19d47e4701de..3fedacdad3b8 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 1f8c9f5acc9f..749a2036f85a 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 5b8c4b8b791c..9e3abfd3344d 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index a7875b201fcc..d08cfad494be 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index eccfdd5cb650..ee164d716b46 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index e69de60eed09..f7ff740db97a 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index c0f7ddfd9b57..aacd8ad6e255 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 01865238fe8e..70bc74f411fa 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.28.Final + 4.1.29.Final-SNAPSHOT netty-transport From f60d08fd32b7287f65077f99a88bac645834098f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 28 Jul 2018 02:53:43 +0800 Subject: [PATCH 107/417] Add test for shading netty-tcnative. (#8157) Motivation: d67d639f5f4de11f32dc15abcd0e18bad5a24a21 added a test for shading the native transport of netty. We should also test that shading netty-tcnative is possible. Modifications: Add test for shading netty-tcnative Result: More testing. --- testsuite-shading/pom.xml | 40 +++++++++++++++++-- .../io/netty/testsuite/shading/ShadingIT.java | 17 +++++--- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index 9e859dc09b13..5b42b7d66241 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -71,7 +71,8 @@ - netty_transport_native_kqueue_${os.detected.arch}.jnilib + netty_transport_native_kqueue_${os.detected.arch}.jnilib + netty_tcnative.jnilib @@ -81,6 +82,20 @@ ${jni.classifier} compile + + + ${project.groupId} + netty-handler + ${project.version} + compile + + + ${project.groupId} + ${tcnative.artifactId} + ${tcnative.version} + ${tcnative.classifier} + compile + @@ -126,7 +141,8 @@ - + + @@ -162,7 +178,8 @@ - netty_transport_native_epoll_${os.detected.arch}.so + netty_transport_native_epoll_${os.detected.arch}.so + netty_tcnative.so @@ -172,6 +189,20 @@ ${jni.classifier} compile + + + ${project.groupId} + netty-handler + ${project.version} + compile + + + ${project.groupId} + ${tcnative.artifactId} + ${tcnative.version} + ${tcnative.classifier} + compile + @@ -217,7 +248,8 @@ - + + diff --git a/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java index 21d02c1ce844..5989581f16b0 100644 --- a/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java +++ b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java @@ -23,14 +23,19 @@ public class ShadingIT { @Test - public void testShadingNativeLibs() throws Exception { + public void testShadingNativeTransport() throws Exception { + testShading0(PlatformDependent.isOsx() ? "io.netty.channel.kqueue.KQueue" : "io.netty.channel.epoll.Epoll"); + } + + @Test + public void testShadingTcnative() throws Exception { + testShading0("io.netty.handler.ssl.OpenSsl"); + } + + private static void testShading0(String classname) throws Exception { String shadingPrefix = System.getProperty("shadingPrefix"); - final Class clazz = Class.forName(shadingPrefix + '.' + className()); + final Class clazz = Class.forName(shadingPrefix + '.' + classname); Method method = clazz.getMethod("ensureAvailability"); method.invoke(null); } - - private static String className() { - return PlatformDependent.isOsx() ? "io.netty.channel.kqueue.KQueue" : "io.netty.channel.epoll.Epoll"; - } } From 630c82717dd8d822323c9e4a8ad51ea4f8a38f86 Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Sun, 29 Jul 2018 03:22:27 -0700 Subject: [PATCH 108/417] Lazy initialize NativeDatagramPacketArray and IovArray in EpollEventLoop (#8160) Motivation: Avoid unnecessary native memory allocation if UDP / TCP isn't being used. Modifications: Create the reused NativeDatagramPacketArray and IovArray upon first use instead of EpollEventLoop construction. Also correct related comment in NativeDatagramPacketArray. Result: Reduced native memory use when using epoll in many cases --- .../netty/channel/epoll/EpollEventLoop.java | 28 +++++++++++++++---- .../epoll/NativeDatagramPacketArray.java | 4 +-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index 09439764bcfb..68e9c125a18e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -61,8 +61,10 @@ final class EpollEventLoop extends SingleThreadEventLoop { private final IntObjectMap channels = new IntObjectHashMap(4096); private final boolean allowGrowing; private final EpollEventArray events; - private final IovArray iovArray = new IovArray(); - private final NativeDatagramPacketArray datagramPacketArray = new NativeDatagramPacketArray(); + + // These are initialized on first use + private IovArray iovArray; + private NativeDatagramPacketArray datagramPacketArray; private final SelectStrategy selectStrategy; private final IntSupplier selectNowSupplier = new IntSupplier() { @@ -144,7 +146,11 @@ public Integer call() throws Exception { * Return a cleared {@link IovArray} that can be used for writes in this {@link EventLoop}. */ IovArray cleanIovArray() { - iovArray.clear(); + if (iovArray == null) { + iovArray = new IovArray(); + } else { + iovArray.clear(); + } return iovArray; } @@ -152,7 +158,11 @@ IovArray cleanIovArray() { * Return a cleared {@link NativeDatagramPacketArray} that can be used for writes in this {@link EventLoop}. */ NativeDatagramPacketArray cleanDatagramPacketArray() { - datagramPacketArray.clear(); + if (datagramPacketArray == null) { + datagramPacketArray = new NativeDatagramPacketArray(); + } else { + datagramPacketArray.clear(); + } return datagramPacketArray; } @@ -458,8 +468,14 @@ protected void cleanup() { } } finally { // release native memory - iovArray.release(); - datagramPacketArray.release(); + if (iovArray != null) { + iovArray.release(); + iovArray = null; + } + if (datagramPacketArray != null) { + datagramPacketArray.release(); + datagramPacketArray = null; + } events.free(); } } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java index c100f3c27a58..0bdb9eb69d7a 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java @@ -100,8 +100,8 @@ void release() { @SuppressWarnings("unused") static final class NativeDatagramPacket { // Each NativeDatagramPackets holds a IovArray which is used for gathering writes. - // This is ok as NativeDatagramPacketArray is always obtained via a FastThreadLocal and - // so the memory needed is quite small anyway. + // This is ok as NativeDatagramPacketArray is always obtained from an EpollEventLoop + // field so the memory needed is quite small anyway. private final IovArray array = new IovArray(); // This is the actual struct iovec* From fe14bad69c706a35e170bcf7c6b218ca4b162b9c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 1 Aug 2018 06:37:53 +0200 Subject: [PATCH 109/417] Adjust SSL related tests to be more correct and so pass in the next EA release of java11. (#8162) Motivation: In some of our tests we not correctly init the SSLEngine before trying to perform a handshake which can cause an IllegalStateException. While this not happened in previous java releases it does now on Java11 (which is "ok" as its even mentioned in the api docs). Beside this how we selected the ciphersuite to test renegotation was not 100 % safe. Modifications: - Correctly init SSLEngine before using it - Correctly select ciphersuite before testing for renegotation. Result: More correct tests and also pass on next java11 EA release. --- .../io/netty/handler/ssl/SslHandlerTest.java | 31 ++++++++++--------- .../SocketSslClientRenegotiateTest.java | 3 +- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java index e982b6a63c3e..75375008f78a 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java @@ -133,6 +133,16 @@ public void testServerHandshakeTimeout() throws Exception { testHandshakeTimeout(false); } + private static SSLEngine newServerModeSSLEngine() throws NoSuchAlgorithmException { + SSLEngine engine = SSLContext.getDefault().createSSLEngine(); + // Set the mode before we try to do the handshake as otherwise it may throw an IllegalStateException. + // See: + // - https://docs.oracle.com/javase/10/docs/api/javax/net/ssl/SSLEngine.html#beginHandshake() + // - http://mail.openjdk.java.net/pipermail/security-dev/2018-July/017715.html + engine.setUseClientMode(false); + return engine; + } + private static void testHandshakeTimeout(boolean client) throws Exception { SSLEngine engine = SSLContext.getDefault().createSSLEngine(); engine.setUseClientMode(client); @@ -155,9 +165,7 @@ private static void testHandshakeTimeout(boolean client) throws Exception { @Test public void testTruncatedPacket() throws Exception { - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - engine.setUseClientMode(false); - + SSLEngine engine = newServerModeSSLEngine(); EmbeddedChannel ch = new EmbeddedChannel(new SslHandler(engine)); // Push the first part of a 5-byte handshake message. @@ -183,9 +191,7 @@ public void testTruncatedPacket() throws Exception { @Test public void testNonByteBufWriteIsReleased() throws Exception { - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - engine.setUseClientMode(false); - + SSLEngine engine = newServerModeSSLEngine(); EmbeddedChannel ch = new EmbeddedChannel(new SslHandler(engine)); AbstractReferenceCounted referenceCounted = new AbstractReferenceCounted() { @@ -210,9 +216,7 @@ protected void deallocate() { @Test(expected = UnsupportedMessageTypeException.class) public void testNonByteBufNotPassThrough() throws Exception { - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - engine.setUseClientMode(false); - + SSLEngine engine = newServerModeSSLEngine(); EmbeddedChannel ch = new EmbeddedChannel(new SslHandler(engine)); try { @@ -224,9 +228,7 @@ public void testNonByteBufNotPassThrough() throws Exception { @Test public void testIncompleteWriteDoesNotCompletePromisePrematurely() throws NoSuchAlgorithmException { - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - engine.setUseClientMode(false); - + SSLEngine engine = newServerModeSSLEngine(); EmbeddedChannel ch = new EmbeddedChannel(new SslHandler(engine)); ChannelPromise promise = ch.newPromise(); @@ -398,7 +400,8 @@ public void channelInactive(ChannelHandlerContext ctx) { @Test public void testCloseFutureNotified() throws Exception { - SslHandler handler = new SslHandler(SSLContext.getDefault().createSSLEngine()); + SSLEngine engine = newServerModeSSLEngine(); + SslHandler handler = new SslHandler(engine); EmbeddedChannel ch = new EmbeddedChannel(handler); ch.close(); @@ -416,7 +419,7 @@ public void testCloseFutureNotified() throws Exception { @Test(timeout = 5000) public void testEventsFired() throws Exception { - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); + SSLEngine engine = newServerModeSSLEngine(); final BlockingQueue events = new LinkedBlockingQueue(); EmbeddedChannel channel = new EmbeddedChannel(new SslHandler(engine), new ChannelInboundHandlerAdapter() { @Override diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java index 855f1914096e..1a49bde78bbc 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java @@ -162,7 +162,8 @@ public void initChannel(Channel sch) throws Exception { Future clientHandshakeFuture = clientSslHandler.handshakeFuture(); clientHandshakeFuture.sync(); - String renegotiation = clientSslHandler.engine().getSupportedCipherSuites()[0]; + String renegotiation = clientSslHandler.engine().getEnabledCipherSuites()[0]; + // Use the first previous enabled ciphersuite and try to renegotiate. clientSslHandler.engine().setEnabledCipherSuites(new String[] { renegotiation }); clientSslHandler.renegotiate().await(); serverChannel.close().awaitUninterruptibly(); From 3ab7cac6209cfa3c2d13929123bb61903fe10f58 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 1 Aug 2018 08:31:31 +0200 Subject: [PATCH 110/417] Disable test as it sometimes fails on the CI Motivation: Temporary disable test that wwas introduced as part of f60d08fd32b7287f65077f99a88bac645834098f as it sometimes fail on the CI. We need to figure out why it fails there (can not reproduce so far even on the CI after ssh into it). Modifications: Ignore test. Result: More stable builds until we figure out the flackyness. --- .../src/test/java/io/netty/testsuite/shading/ShadingIT.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java index 5989581f16b0..1606bfcc9c10 100644 --- a/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java +++ b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java @@ -16,6 +16,7 @@ package io.netty.testsuite.shading; import io.netty.util.internal.PlatformDependent; +import org.junit.Ignore; import org.junit.Test; import java.lang.reflect.Method; @@ -27,6 +28,7 @@ public void testShadingNativeTransport() throws Exception { testShading0(PlatformDependent.isOsx() ? "io.netty.channel.kqueue.KQueue" : "io.netty.channel.epoll.Epoll"); } + @Ignore("Figure out why this sometimes fail on the CI") @Test public void testShadingTcnative() throws Exception { testShading0("io.netty.handler.ssl.OpenSsl"); From 44d3753c481d61a83097fbbee681512aa8833da8 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 2 Aug 2018 21:42:21 +0200 Subject: [PATCH 111/417] Fix NPE exception when using invalid cipher during building SslContext. (#8171) Motivation: We missed to do a null check before trying to destroy the OpenSslSessionContext, which could lead to a NPE. Modifications: Add null check and tests. Result: Fix https://github.com/netty/netty/issues/8170. --- .../ssl/ReferenceCountedOpenSslContext.java | 6 ++++- .../handler/ssl/SslContextBuilderTest.java | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index 4a0202267767..18c86795744d 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -486,7 +486,11 @@ private void destroy() { SSLContext.free(ctx); ctx = 0; - sessionContext().destroy(); + + OpenSslSessionContext context = sessionContext(); + if (context != null) { + context.destroy(); + } } } finally { writerLock.unlock(); diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java index 752424cfb557..0a9429e9c1a8 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java @@ -24,6 +24,8 @@ import org.junit.Test; import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import java.util.Collections; public class SslContextBuilderTest { @@ -71,6 +73,30 @@ public void testServerContextOpenssl() throws Exception { testServerContext(SslProvider.OPENSSL); } + @Test(expected = IllegalArgumentException.class) + public void testInvalidCipherJdk() throws Exception { + Assume.assumeTrue(OpenSsl.isAvailable()); + testInvalidCipher(SslProvider.JDK); + } + + @Test(expected = SSLException.class) + public void testInvalidCipherOpenSSL() throws Exception { + Assume.assumeTrue(OpenSsl.isAvailable()); + testInvalidCipher(SslProvider.OPENSSL); + } + + private static void testInvalidCipher(SslProvider provider) throws Exception { + SelfSignedCertificate cert = new SelfSignedCertificate(); + SslContextBuilder builder = SslContextBuilder.forClient() + .sslProvider(provider) + .ciphers(Collections.singleton("SOME_INVALID_CIPHER")) + .keyManager(cert.certificate(), + cert.privateKey()) + .trustManager(cert.certificate()); + SslContext context = builder.build(); + context.newEngine(UnpooledByteBufAllocator.DEFAULT); + } + private static void testClientContextFromFile(SslProvider provider) throws Exception { SelfSignedCertificate cert = new SelfSignedCertificate(); SslContextBuilder builder = SslContextBuilder.forClient() From 2c13f71c733c5778cd359c9148f50e63d1878f7f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 3 Aug 2018 07:07:09 +0200 Subject: [PATCH 112/417] Ensure NIO transport can be used on Java6 again. (#8168) Motivation: 952eeb8e1e3706b09d4d3f32f16d7f0e5c540cb5 introduced the possibility to use any JDK SocketOption when using the NIO transport but broke the possibility to use netty with java6. Modifications: Do not use java7 types in method signatures of the static methods in NioChannelOption to prevent class-loader issues on java6. Result: Fixes https://github.com/netty/netty/issues/8166. --- .../channel/socket/nio/NioChannelOption.java | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java b/transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java index 7d94b99706ef..a56e07b382f6 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java @@ -19,33 +19,39 @@ import io.netty.channel.ChannelOption; import java.io.IOException; -import java.net.SocketOption; -import java.nio.channels.NetworkChannel; +import java.nio.channels.Channel; import java.util.Set; /** - * Provides {@link ChannelOption} over a given {@link SocketOption} which is then passed through the underlying - * {@link NetworkChannel}. + * Provides {@link ChannelOption} over a given {@link java.net.SocketOption} which is then passed through the underlying + * {@link java.nio.channels.NetworkChannel}. */ public final class NioChannelOption extends ChannelOption { - private final SocketOption option; + private final java.net.SocketOption option; @SuppressWarnings("deprecation") - private NioChannelOption(SocketOption option) { + private NioChannelOption(java.net.SocketOption option) { super(option.name()); this.option = option; } /** - * Returns a {@link ChannelOption} for the given {@link SocketOption}. + * Returns a {@link ChannelOption} for the given {@link java.net.SocketOption}. */ - public static ChannelOption of(SocketOption option) { + public static ChannelOption of(java.net.SocketOption option) { return new NioChannelOption(option); } + // It's important to not use java.nio.channels.NetworkChannel as otherwise the classes that sometimes call this + // method may not be used on Java 6, as method linking can happen eagerly even if this method was not actually + // called at runtime. + // + // See https://github.com/netty/netty/issues/8166 + // Internal helper methods to remove code duplication between Nio*Channel implementations. - static boolean setOption(NetworkChannel channel, NioChannelOption option, T value) { + static boolean setOption(Channel jdkChannel, NioChannelOption option, T value) { + java.nio.channels.NetworkChannel channel = (java.nio.channels.NetworkChannel) jdkChannel; if (!channel.supportedOptions().contains(option.option)) { return false; } @@ -57,7 +63,9 @@ static boolean setOption(NetworkChannel channel, NioChannelOption option, } } - static T getOption(NetworkChannel channel, NioChannelOption option) { + static T getOption(Channel jdkChannel, NioChannelOption option) { + java.nio.channels.NetworkChannel channel = (java.nio.channels.NetworkChannel) jdkChannel; + if (!channel.supportedOptions().contains(option.option)) { return null; } @@ -69,12 +77,13 @@ static T getOption(NetworkChannel channel, NioChannelOption option) { } @SuppressWarnings("unchecked") - static ChannelOption[] getOptions(NetworkChannel channel) { - Set> supportedOpts = channel.supportedOptions(); + static ChannelOption[] getOptions(Channel jdkChannel) { + java.nio.channels.NetworkChannel channel = (java.nio.channels.NetworkChannel) jdkChannel; + Set> supportedOpts = channel.supportedOptions(); ChannelOption[] extraOpts = new ChannelOption[supportedOpts.size()]; int i = 0; - for (SocketOption opt : supportedOpts) { + for (java.net.SocketOption opt : supportedOpts) { extraOpts[i++] = new NioChannelOption(opt); } return extraOpts; From 55fec94592920d8696349fd2956039e87cc53bc7 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 6 Aug 2018 08:31:17 +0200 Subject: [PATCH 113/417] Also clear the authoritativeDnsServerCache when closing the Channel. (#8174) Motivation: At the moment we only clear the resolveCache when the Channel is closed. We should also do the same for the authoritativeDnsServerCache. Modifications: Add authoritativeDnsServerCache.clear() to the Channel closeFuture. Result: Correctly clear all caches. --- .../src/main/java/io/netty/resolver/dns/DnsNameResolver.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java index 5619e176efcd..eaab795d5d4d 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java @@ -219,7 +219,7 @@ public DnsNameResolver( EventLoop eventLoop, ChannelFactory channelFactory, final DnsCache resolveCache, - DnsCache authoritativeDnsServerCache, + final DnsCache authoritativeDnsServerCache, DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory, long queryTimeoutMillis, ResolvedAddressTypes resolvedAddressTypes, @@ -306,8 +306,9 @@ protected void initChannel(DatagramChannel ch) throws Exception { ch.closeFuture().addListener(new ChannelFutureListener() { @Override - public void operationComplete(ChannelFuture future) throws Exception { + public void operationComplete(ChannelFuture future) { resolveCache.clear(); + authoritativeDnsServerCache.clear(); } }); } From 0bea8ecf5d851b108f788eec21a2c21098e04f99 Mon Sep 17 00:00:00 2001 From: vincent-grosbois Date: Tue, 7 Aug 2018 11:31:24 +0200 Subject: [PATCH 114/417] CompositeByteBuf nioBuffer doesn't always alloc (#8176) In nioBuffer(int,int) in CompositeByteBuf , we create a sub-array of nioBuffers for the components that are in range, then concatenate all the components in range into a single bigger buffer. However, if the call to nioBuffers() returned only one sub-buffer, then we are copying it to a newly-allocated buffer "merged" for no reason. Motivation: Profiler for Spark shows a lot of time spent in put() method inside nioBuffer(), while usually no copy of data is required. Modification: This change skips this last step and just returns a duplicate of the single buffer returned by the call to nioBuffers(), which will in most implementation not copy the data Result: No copy when the source is only 1 buffer --- buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 3d6fd215b937..85160c79a6e6 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -1467,9 +1467,13 @@ public ByteBuffer nioBuffer(int index, int length) { } } - ByteBuffer merged = ByteBuffer.allocate(length).order(order()); ByteBuffer[] buffers = nioBuffers(index, length); + if (buffers.length == 1) { + return buffers[0].duplicate(); + } + + ByteBuffer merged = ByteBuffer.allocate(length).order(order()); for (ByteBuffer buf: buffers) { merged.put(buf); } From b3b04d0de2793b9964efc8eac9b95d559d02d679 Mon Sep 17 00:00:00 2001 From: Scott Mitchell <7562868+Scottmitch@users.noreply.github.com> Date: Tue, 7 Aug 2018 23:14:18 -0700 Subject: [PATCH 115/417] DnsNameResolver hangs if search domain results in invalid hostname (#8180) Motivation: DnsNameResolver manages search domains and will retry the request with the different search domains provided to it. However if the query results in an invalid hostname, the Future corresponding to the resolve request will never be completed. Modifications: - If a resolve attempt results in an invalid hostname and the query isn't issued we should fail the associated promise Result: No more hang from DnsNameResolver if search domain results in invalid hostname. --- .../netty/resolver/dns/DnsResolveContext.java | 21 ++++++------- .../resolver/dns/DnsNameResolverTest.java | 30 +++++++++++++++++++ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java index bc63e710e14d..dbb1ab6bbffb 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java @@ -749,7 +749,7 @@ private void followCname(DnsQuestion question, String cname, DnsQueryLifecycleOb final DnsQuestion cnameQuestion; try { - cnameQuestion = newQuestion(cname, question.type()); + cnameQuestion = new DefaultDnsQuestion(cname, question.type(), dnsClass); } catch (Throwable cause) { queryLifecycleObserver.queryFailed(cause); PlatformDependent.throwException(cause); @@ -760,23 +760,20 @@ private void followCname(DnsQuestion question, String cname, DnsQueryLifecycleOb private boolean query(String hostname, DnsRecordType type, DnsServerAddressStream dnsServerAddressStream, Promise> promise) { - final DnsQuestion question = newQuestion(hostname, type); - if (question == null) { + final DnsQuestion question; + try { + question = new DefaultDnsQuestion(hostname, type, dnsClass); + } catch (Throwable cause) { + // Assume a single failure means that queries will succeed. If the hostname is invalid for one type + // there is no case where it is known to be valid for another type. + promise.tryFailure(new IllegalArgumentException("Unable to create DNS Question for: [" + hostname + ", " + + type + "]", cause)); return false; } query(dnsServerAddressStream, 0, question, promise, null); return true; } - private DnsQuestion newQuestion(String hostname, DnsRecordType type) { - try { - return new DefaultDnsQuestion(hostname, type, dnsClass); - } catch (IllegalArgumentException e) { - // java.net.IDN.toASCII(...) may throw an IllegalArgumentException if it fails to parse the hostname - return null; - } - } - /** * Holds the closed DNS Servers for a domain. */ diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java index dcda33c8d5a7..36fcdf4842d8 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java @@ -58,7 +58,9 @@ import org.hamcrest.Matchers; import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import java.io.IOException; import java.net.InetAddress; @@ -88,6 +90,7 @@ import static io.netty.handler.codec.dns.DnsRecordType.CNAME; import static io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.DNS_PORT; import static io.netty.resolver.dns.DnsServerAddresses.sequential; +import static java.util.Collections.singletonList; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; @@ -298,6 +301,9 @@ public class DnsNameResolverTest { private static final TestDnsServer dnsServer = new TestDnsServer(DOMAINS_ALL); private static final EventLoopGroup group = new NioEventLoopGroup(1); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + private static DnsNameResolverBuilder newResolver(boolean decodeToUnicode) { return newResolver(decodeToUnicode, null); } @@ -1682,4 +1688,28 @@ public Set getRecords(QuestionRecord question) { } } } + + @Test + public void testSearchDomainQueryFailureForSingleAddressTypeCompletes() { + expectedException.expect(UnknownHostException.class); + testSearchDomainQueryFailureCompletes(ResolvedAddressTypes.IPV4_ONLY); + } + + @Test + public void testSearchDomainQueryFailureForMultipleAddressTypeCompletes() { + expectedException.expect(UnknownHostException.class); + testSearchDomainQueryFailureCompletes(ResolvedAddressTypes.IPV4_PREFERRED); + } + + private void testSearchDomainQueryFailureCompletes(ResolvedAddressTypes types) { + DnsNameResolver resolver = newResolver() + .resolvedAddressTypes(types) + .ndots(1) + .searchDomains(singletonList(".")).build(); + try { + resolver.resolve("invalid.com").syncUninterruptibly(); + } finally { + resolver.close(); + } + } } From 534de73d284fb27c4ecb83a9b3e56443b737ba59 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 9 Aug 2018 13:11:08 +0200 Subject: [PATCH 116/417] Workaround JDK bug that will cause an AssertionError when calling ServerSocketChannel.config().getOptions(). (#8183) Motivation: There is a JDK bug which will return IP_TOS as supported option for ServerSocketChannel even if its not supported afterwards and cause an AssertionError. See http://mail.openjdk.java.net/pipermail/nio-dev/2018-August/005365.html. Modifications: Add a workaround for the JDK bug. Result: ServerSocketChannel.config().getOptions() will not throw anymore and work as expected. --- pom.xml | 1 + .../channel/socket/nio/NioChannelOption.java | 37 ++++++++++++++++--- .../socket/nio/AbstractNioChannelTest.java | 10 +++++ 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 8b5a4f23fbd9..6eb819cfeafa 100644 --- a/pom.xml +++ b/pom.xml @@ -703,6 +703,7 @@ java.net.StandardProtocolFamily java.nio.channels.spi.SelectorProvider java.net.SocketOption + java.net.StandardSocketOptions java.nio.channels.NetworkChannel diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java b/transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java index a56e07b382f6..3f9550f101c7 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioChannelOption.java @@ -20,6 +20,9 @@ import java.io.IOException; import java.nio.channels.Channel; +import java.nio.channels.ServerSocketChannel; +import java.util.ArrayList; +import java.util.List; import java.util.Set; /** @@ -55,6 +58,11 @@ static boolean setOption(Channel jdkChannel, NioChannelOption option, T v if (!channel.supportedOptions().contains(option.option)) { return false; } + if (channel instanceof ServerSocketChannel && option.option == java.net.StandardSocketOptions.IP_TOS) { + // Skip IP_TOS as a workaround for a JDK bug: + // See http://mail.openjdk.java.net/pipermail/nio-dev/2018-August/005365.html + return false; + } try { channel.setOption(option.option, value); return true; @@ -69,6 +77,11 @@ static T getOption(Channel jdkChannel, NioChannelOption option) { if (!channel.supportedOptions().contains(option.option)) { return null; } + if (channel instanceof ServerSocketChannel && option.option == java.net.StandardSocketOptions.IP_TOS) { + // Skip IP_TOS as a workaround for a JDK bug: + // See http://mail.openjdk.java.net/pipermail/nio-dev/2018-August/005365.html + return null; + } try { return channel.getOption(option.option); } catch (IOException e) { @@ -80,12 +93,26 @@ static T getOption(Channel jdkChannel, NioChannelOption option) { static ChannelOption[] getOptions(Channel jdkChannel) { java.nio.channels.NetworkChannel channel = (java.nio.channels.NetworkChannel) jdkChannel; Set> supportedOpts = channel.supportedOptions(); - ChannelOption[] extraOpts = new ChannelOption[supportedOpts.size()]; - int i = 0; - for (java.net.SocketOption opt : supportedOpts) { - extraOpts[i++] = new NioChannelOption(opt); + if (channel instanceof ServerSocketChannel) { + List> extraOpts = new ArrayList>(supportedOpts.size()); + for (java.net.SocketOption opt : supportedOpts) { + if (opt == java.net.StandardSocketOptions.IP_TOS) { + // Skip IP_TOS as a workaround for a JDK bug: + // See http://mail.openjdk.java.net/pipermail/nio-dev/2018-August/005365.html + continue; + } + extraOpts.add(new NioChannelOption(opt)); + } + return extraOpts.toArray(new ChannelOption[0]); + } else { + ChannelOption[] extraOpts = new ChannelOption[supportedOpts.size()]; + + int i = 0; + for (java.net.SocketOption opt : supportedOpts) { + extraOpts[i++] = new NioChannelOption(opt); + } + return extraOpts; } - return extraOpts; } } diff --git a/transport/src/test/java/io/netty/channel/socket/nio/AbstractNioChannelTest.java b/transport/src/test/java/io/netty/channel/socket/nio/AbstractNioChannelTest.java index c2a8c43dcc2a..5652a4ebac3a 100644 --- a/transport/src/test/java/io/netty/channel/socket/nio/AbstractNioChannelTest.java +++ b/transport/src/test/java/io/netty/channel/socket/nio/AbstractNioChannelTest.java @@ -66,4 +66,14 @@ public void testInvalidNioChannelOption() { channel.unsafe().closeForcibly(); } } + + @Test + public void testGetOptions() { + T channel = newNioChannel(); + try { + channel.config().getOptions(); + } finally { + channel.unsafe().closeForcibly(); + } + } } From dbc9ec1ab2cd5b9bccef1b85aa308c3e7e58022a Mon Sep 17 00:00:00 2001 From: Lai Jiang Date: Thu, 9 Aug 2018 15:30:02 -0400 Subject: [PATCH 117/417] Set SNI servernames in OpenSSL engine when created in client mode (#8178) Motivation: When using the JDK SSL provider in client mode, the SNI host names (called serverNames in SslEngineImpl) is set to the peerHost (if available) that is used to initialize the SSL Engine: http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/security/ssl/SSLEngineImpl.java#l377 This allows one to call SslEngine.getSSLParameters() and inspect what is the SNI name to be sent. The same should be done in the OpenSSL provider as well. Currently even though the the SNI name is sent by the OpenSSL provider during handshake when the peerHost is specified, it is missing from the parameters. Modification: Set the sniHostNames field when SNI is to be used. Also verifies the peer is actually a hostname before setting it as the SNI name, which is consistent with JDK SSL provider's behavior. Result: SslEngine using the OpenSSL provider created in client mode with peerHost will initialize sniHostNames with the peerHost. Calling SslEngine.getSSLParameters().getServerNames() will return a list that contains that name. --- .../handler/ssl/ReferenceCountedOpenSslEngine.java | 7 ++++--- .../src/main/java/io/netty/handler/ssl/SslUtils.java | 12 ++++++++++++ .../java/io/netty/handler/ssl/SSLEngineTest.java | 5 +++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 8b1473576675..f902e000b73a 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -20,7 +20,6 @@ import io.netty.internal.tcnative.Buffer; import io.netty.internal.tcnative.SSL; import io.netty.util.AbstractReferenceCounted; -import io.netty.util.CharsetUtil; import io.netty.util.ReferenceCounted; import io.netty.util.ResourceLeakDetector; import io.netty.util.ResourceLeakDetectorFactory; @@ -40,6 +39,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -272,10 +272,11 @@ protected void deallocate() { setEnabledProtocols(context.protocols); } - // Use SNI if peerHost was specified + // Use SNI if peerHost was specified and a valid hostname // See https://github.com/netty/netty/issues/4746 - if (clientMode && peerHost != null) { + if (clientMode && SslUtils.isValidHostNameForSNI(peerHost)) { SSL.setTlsExtHostName(ssl, peerHost); + sniHostNames = Collections.singletonList(peerHost); } if (enableOcsp) { diff --git a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java b/handler/src/main/java/io/netty/handler/ssl/SslUtils.java index 3dd40fdf37be..414c9d1d18f8 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslUtils.java @@ -21,6 +21,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.base64.Base64Dialect; +import io.netty.util.NetUtil; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -349,6 +350,17 @@ static ByteBuf toBase64(ByteBufAllocator allocator, ByteBuf src) { return dst; } + /** + * Validate that the given hostname can be used in SNI extension. + */ + static boolean isValidHostNameForSNI(String hostname) { + return hostname != null && + hostname.indexOf('.') > 0 && + !hostname.endsWith(".") && + !NetUtil.isValidIpV4Address(hostname) && + !NetUtil.isValidIpV6Address(hostname); + } + private SslUtils() { } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 1e66efc6f8fb..c35108875406 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -71,6 +71,7 @@ import java.util.concurrent.TimeUnit; import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SNIHostName; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLException; @@ -834,6 +835,10 @@ protected void initChannel(Channel ch) throws Exception { InetSocketAddress remoteAddress = (InetSocketAddress) serverChannel.localAddress(); SslHandler sslHandler = clientSslCtx.newHandler(ch.alloc(), expectedHost, 0); SSLParameters parameters = sslHandler.engine().getSSLParameters(); + if (SslUtils.isValidHostNameForSNI(expectedHost)) { + assertEquals(1, parameters.getServerNames().size()); + assertEquals(new SNIHostName(expectedHost), parameters.getServerNames().get(0)); + } parameters.setEndpointIdentificationAlgorithm("HTTPS"); sslHandler.engine().setSSLParameters(parameters); p.addLast(sslHandler); From 56eb1e92cc1d12182c6ed0344d144d29bbeea0d1 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 10 Aug 2018 08:46:15 +0200 Subject: [PATCH 118/417] Add tests to verify caches are cleared when the resolver is closed. (#8186) Motivation: 55fec94592920d8696349fd2956039e87cc53bc7 fixed a bug where we did not correctly clear all caches when the resolver was closed but did not add a testcase. Modifications: Add testcase. Result: More tests. --- .../resolver/dns/DnsNameResolverTest.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java index 36fcdf4842d8..e9883e6ed130 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java @@ -82,6 +82,7 @@ import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -1712,4 +1713,70 @@ private void testSearchDomainQueryFailureCompletes(ResolvedAddressTypes types) { resolver.close(); } } + + @Test(timeout = 2000L) + public void testCachesClearedOnClose() throws Exception { + final CountDownLatch resolveLatch = new CountDownLatch(1); + final CountDownLatch authoritativeLatch = new CountDownLatch(1); + + DnsNameResolver resolver = newResolver().resolveCache(new DnsCache() { + @Override + public void clear() { + resolveLatch.countDown(); + } + + @Override + public boolean clear(String hostname) { + return false; + } + + @Override + public List get(String hostname, DnsRecord[] additionals) { + return null; + } + + @Override + public DnsCacheEntry cache( + String hostname, DnsRecord[] additionals, InetAddress address, long originalTtl, EventLoop loop) { + return null; + } + + @Override + public DnsCacheEntry cache( + String hostname, DnsRecord[] additionals, Throwable cause, EventLoop loop) { + return null; + } + }) + .authoritativeDnsServerCache(new DnsCache() { + @Override + public void clear() { + authoritativeLatch.countDown(); + } + + @Override + public boolean clear(String hostname) { + return false; + } + + @Override + public List get(String hostname, DnsRecord[] additionals) { + return null; + } + + @Override + public DnsCacheEntry cache( + String hostname, DnsRecord[] additionals, InetAddress address, long originalTtl, EventLoop loop) { + return null; + } + + @Override + public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, Throwable cause, EventLoop loop) { + return null; + } + }).build(); + + resolver.close(); + resolveLatch.await(); + authoritativeLatch.await(); + } } From f22781f176ae5bee3f2886783586f6abfdda5faf Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 10 Aug 2018 08:53:59 +0200 Subject: [PATCH 119/417] Correctly handle hostnames with and without trailing dot in the DefaultDnsCache and use it for searchdomains. (#8181) Motivation: We should ensure we return the same cached entries for the hostname and hostname ending with dot. Beside this we also should use it for the searchdomains as well. Modifications: - Internally always use hostname with a dot as a key and so ensure we correctly handle it in the cache. - Also query the cache for each searchdomain - Add unit tests Result: Use the same cached entries for hostname with and without trailing dot. Query the cache for each searchdomain query as well --- .../netty/resolver/dns/DefaultDnsCache.java | 30 +++++----- .../dns/DnsAddressResolveContext.java | 9 +++ .../netty/resolver/dns/DnsNameResolver.java | 2 +- .../netty/resolver/dns/DnsResolveContext.java | 14 +++-- .../resolver/dns/DefaultDnsCacheTest.java | 26 ++++++++ .../resolver/dns/DnsNameResolverTest.java | 60 ++++++++++++++++--- 6 files changed, 111 insertions(+), 30 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCache.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCache.java index d5431362fec0..6116f5c1eb44 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCache.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCache.java @@ -19,6 +19,7 @@ import io.netty.handler.codec.dns.DnsRecord; import io.netty.util.concurrent.ScheduledFuture; import io.netty.util.internal.PlatformDependent; +import io.netty.util.internal.StringUtil; import io.netty.util.internal.UnstableApi; import java.net.InetAddress; @@ -115,7 +116,7 @@ public void clear() { @Override public boolean clear(String hostname) { checkNotNull(hostname, "hostname"); - Entries entries = resolveCache.remove(hostname); + Entries entries = resolveCache.remove(appendDot(hostname)); return entries != null && entries.clearAndCancel(); } @@ -130,7 +131,7 @@ public List get(String hostname, DnsRecord[] additional return Collections.emptyList(); } - Entries entries = resolveCache.get(hostname); + Entries entries = resolveCache.get(appendDot(hostname)); return entries == null ? null : entries.get(); } @@ -144,7 +145,8 @@ public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, if (maxTtl == 0 || !emptyAdditionals(additionals)) { return e; } - cache0(e, Math.max(minTtl, Math.min(MAX_SUPPORTED_TTL_SECS, (int) Math.min(maxTtl, originalTtl))), loop); + cache0(appendDot(hostname), e, + Math.max(minTtl, Math.min(MAX_SUPPORTED_TTL_SECS, (int) Math.min(maxTtl, originalTtl))), loop); return e; } @@ -159,25 +161,25 @@ public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, Throwable c return e; } - cache0(e, Math.min(MAX_SUPPORTED_TTL_SECS, negativeTtl), loop); + cache0(appendDot(hostname), e, Math.min(MAX_SUPPORTED_TTL_SECS, negativeTtl), loop); return e; } - private void cache0(DefaultDnsCacheEntry e, int ttl, EventLoop loop) { - Entries entries = resolveCache.get(e.hostname()); + private void cache0(String hostname, DefaultDnsCacheEntry e, int ttl, EventLoop loop) { + Entries entries = resolveCache.get(hostname); if (entries == null) { entries = new Entries(e); - Entries oldEntries = resolveCache.putIfAbsent(e.hostname(), entries); + Entries oldEntries = resolveCache.putIfAbsent(hostname, entries); if (oldEntries != null) { entries = oldEntries; } } entries.add(e); - scheduleCacheExpiration(e, ttl, loop); + scheduleCacheExpiration(hostname, e, ttl, loop); } - private void scheduleCacheExpiration(final DefaultDnsCacheEntry e, + private void scheduleCacheExpiration(final String hostname, final DefaultDnsCacheEntry e, int ttl, EventLoop loop) { e.scheduleExpiration(loop, new Runnable() { @@ -192,7 +194,7 @@ public void run() { // completely fine to remove the entry even if the TTL is not reached yet. // // See https://github.com/netty/netty/issues/7329 - Entries entries = resolveCache.remove(e.hostname); + Entries entries = resolveCache.remove(hostname); if (entries != null) { entries.clearAndCancel(); } @@ -239,10 +241,6 @@ public Throwable cause() { return cause; } - String hostname() { - return hostname; - } - void scheduleExpiration(EventLoop loop, Runnable task, long delay, TimeUnit unit) { assert expirationFuture == null : "expiration task scheduled already"; expirationFuture = loop.schedule(task, delay, unit); @@ -338,4 +336,8 @@ private static void cancelExpiration(List entryList) { } } } + + private static String appendDot(String hostname) { + return StringUtil.endsWith(hostname, '.') ? hostname : hostname + '.'; + } } diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolveContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolveContext.java index 63490efa4ad9..98c474bbb165 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolveContext.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolveContext.java @@ -25,6 +25,7 @@ import io.netty.channel.EventLoop; import io.netty.handler.codec.dns.DnsRecord; import io.netty.handler.codec.dns.DnsRecordType; +import io.netty.util.concurrent.Promise; final class DnsAddressResolveContext extends DnsResolveContext { @@ -84,4 +85,12 @@ void cache(String hostname, DnsRecord[] additionals, void cache(String hostname, DnsRecord[] additionals, UnknownHostException cause) { resolveCache.cache(hostname, additionals, cause, parent.ch.eventLoop()); } + + @Override + void doSearchDomainQuery(String hostname, Promise> nextPromise) { + // Query the cache for the hostname first and only do a query if we could not find it in the cache. + if (!parent.doResolveAllCached(hostname, additionals, nextPromise, resolveCache)) { + super.doSearchDomainQuery(hostname, nextPromise); + } + } } diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java index eaab795d5d4d..06962f37038d 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java @@ -790,7 +790,7 @@ protected void doResolveAll(String inetHost, } } - private boolean doResolveAllCached(String hostname, + boolean doResolveAllCached(String hostname, DnsRecord[] additionals, Promise> promise, DnsCache resolveCache) { diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java index dbb1ab6bbffb..b3220c39e89d 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java @@ -97,7 +97,7 @@ public void operationComplete(Future>> queriesInProgress = Collections.newSetFromMap( @@ -164,7 +164,8 @@ void resolve(final Promise> promise) { final String initialHostname = startWithoutSearchDomain ? hostname : hostname + '.' + searchDomains[0]; final int initialSearchDomainIdx = startWithoutSearchDomain ? 0 : 1; - doSearchDomainQuery(initialHostname, new FutureListener>() { + final Promise> searchDomainPromise = parent.executor().newPromise(); + searchDomainPromise.addListener(new FutureListener>() { private int searchDomainIdx = initialSearchDomainIdx; @Override public void operationComplete(Future> future) { @@ -175,7 +176,9 @@ public void operationComplete(Future> future) { if (DnsNameResolver.isTransportOrTimeoutError(cause)) { promise.tryFailure(new SearchDomainUnknownHostException(cause, hostname)); } else if (searchDomainIdx < searchDomains.length) { - doSearchDomainQuery(hostname + '.' + searchDomains[searchDomainIdx++], this); + Promise> newPromise = parent.executor().newPromise(); + newPromise.addListener(this); + doSearchDomainQuery(hostname + '.' + searchDomains[searchDomainIdx++], newPromise); } else if (!startWithoutSearchDomain) { internalResolve(promise); } else { @@ -184,6 +187,7 @@ public void operationComplete(Future> future) { } } }); + doSearchDomainQuery(initialHostname, searchDomainPromise); } } @@ -213,12 +217,10 @@ public Throwable fillInStackTrace() { } } - private void doSearchDomainQuery(String hostname, FutureListener> listener) { + void doSearchDomainQuery(String hostname, Promise> nextPromise) { DnsResolveContext nextContext = newResolverContext(parent, hostname, dnsClass, expectedTypes, additionals, nameServerAddrs); - Promise> nextPromise = parent.executor().newPromise(); nextContext.internalResolve(nextPromise); - nextPromise.addListener(listener); } private void internalResolve(Promise> promise) { diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultDnsCacheTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultDnsCacheTest.java index 17aba98e3c25..d3b2384c7193 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultDnsCacheTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultDnsCacheTest.java @@ -172,4 +172,30 @@ public void testCacheFailed() throws Exception { group.shutdownGracefully(); } } + + @Test + public void testDotHandling() throws Exception { + InetAddress addr1 = InetAddress.getByAddress(new byte[] { 10, 0, 0, 1 }); + InetAddress addr2 = InetAddress.getByAddress(new byte[] { 10, 0, 0, 2 }); + EventLoopGroup group = new DefaultEventLoopGroup(1); + + try { + EventLoop loop = group.next(); + final DefaultDnsCache cache = new DefaultDnsCache(1, 100, 100); + cache.cache("netty.io", null, addr1, 10000, loop); + cache.cache("netty.io.", null, addr2, 10000, loop); + + List entries = cache.get("netty.io", null); + assertEquals(2, entries.size()); + assertEntry(entries.get(0), addr1); + assertEntry(entries.get(1), addr2); + + List entries2 = cache.get("netty.io.", null); + assertEquals(2, entries2.size()); + assertEntry(entries2.get(0), addr1); + assertEntry(entries2.get(1), addr2); + } finally { + group.shutdownGracefully(); + } + } } diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java index e9883e6ed130..e1b3c051b08c 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java @@ -96,13 +96,7 @@ import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; public class DnsNameResolverTest { @@ -1746,8 +1740,7 @@ public DnsCacheEntry cache( String hostname, DnsRecord[] additionals, Throwable cause, EventLoop loop) { return null; } - }) - .authoritativeDnsServerCache(new DnsCache() { + }).authoritativeDnsServerCache(new DnsCache() { @Override public void clear() { authoritativeLatch.countDown(); @@ -1779,4 +1772,53 @@ public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, Throwable c resolveLatch.await(); authoritativeLatch.await(); } + + @Test + public void testResolveACachedWithDot() { + final DnsCache cache = new DefaultDnsCache(); + DnsNameResolver resolver = newResolver(ResolvedAddressTypes.IPV4_ONLY) + .resolveCache(cache).build(); + + try { + String domain = DOMAINS.iterator().next(); + String domainWithDot = domain + '.'; + + resolver.resolve(domain).syncUninterruptibly(); + List cached = cache.get(domain, null); + List cached2 = cache.get(domainWithDot, null); + + assertEquals(1, cached.size()); + assertSame(cached, cached2); + } finally { + resolver.close(); + } + } + + @Test + public void testResolveACachedWithDotSearchDomain() throws Exception { + final TestDnsCache cache = new TestDnsCache(new DefaultDnsCache()); + TestDnsServer server = new TestDnsServer(Collections.singleton("test.netty.io")); + server.start(); + DnsNameResolver resolver = newResolver(ResolvedAddressTypes.IPV4_ONLY) + .searchDomains(Collections.singletonList("netty.io")) + .nameServerProvider(new SingletonDnsServerAddressStreamProvider(server.localAddress())) + .resolveCache(cache).build(); + try { + resolver.resolve("test").syncUninterruptibly(); + + assertNull(cache.cacheHits.get("test.netty.io")); + + List cached = cache.cache.get("test.netty.io", null); + List cached2 = cache.cache.get("test.netty.io.", null); + assertEquals(1, cached.size()); + assertSame(cached, cached2); + + resolver.resolve("test").syncUninterruptibly(); + List entries = cache.cacheHits.get("test.netty.io"); + assertFalse(entries.isEmpty()); + } finally { + resolver.close(); + server.stop(); + } + } } From bd25fd03e3ca02e7eeee17da6d51ba05e464f0b4 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 10 Aug 2018 08:54:21 +0200 Subject: [PATCH 120/417] Add testcase for ChannelInitializer.initChannel(...) when throwing an Exception (#8188) Motivation: We had a report that the exception may not be correctly propagated. This test shows it is. Modifications: Add testcase. Result: Test for https://github.com/netty/netty/issues/8158 --- .../netty/channel/ChannelInitializerTest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java b/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java index 5ab48867bc0b..26b5e4e9fcf4 100644 --- a/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java +++ b/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java @@ -30,6 +30,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -63,6 +64,47 @@ public void tearDown() { group.shutdownGracefully(0, TIMEOUT_MILLIS, TimeUnit.MILLISECONDS).syncUninterruptibly(); } + @Test + public void testInitChannelThrowsRegisterFirst() { + testInitChannelThrows(true); + } + + @Test + public void testInitChannelThrowsRegisterAfter() { + testInitChannelThrows(false); + } + + private void testInitChannelThrows(boolean registerFirst) { + final Exception exception = new Exception(); + final AtomicReference causeRef = new AtomicReference(); + + ChannelPipeline pipeline = new LocalChannel().pipeline(); + + if (registerFirst) { + group.register(pipeline.channel()).syncUninterruptibly(); + } + pipeline.addFirst(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) throws Exception { + throw exception; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + causeRef.set(cause); + super.exceptionCaught(ctx, cause); + } + }); + + if (!registerFirst) { + group.register(pipeline.channel()).syncUninterruptibly(); + } + pipeline.channel().close().syncUninterruptibly(); + pipeline.channel().closeFuture().syncUninterruptibly(); + + assertSame(exception, causeRef.get()); + } + @Test public void testChannelInitializerInInitializerCorrectOrdering() { final ChannelInboundHandlerAdapter handler1 = new ChannelInboundHandlerAdapter(); From 2fa7a0aa57f085079d66f5a8c0b807da12fc75fc Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 15 Aug 2018 09:07:13 +0200 Subject: [PATCH 121/417] Keep the amount of scheduled tasks for DefaultDnsCache at a minimum (#8187) Motivation: We are currently always remove all entries from the cache for a hostname if the lowest TTL was reached but schedule one for each of the cached entries. This is wasteful. Modifications: - Reimplement logic to schedule TTL to only schedule a new removal task if the requested TTL was actual lower then the one for the already scheduled task. - Ensure we only remove from the internal map if we did not replace the Entries in the meantime. Result: Less overhead in terms of scheduled tasks for the DefaultDnsCache --- .../netty/resolver/dns/DefaultDnsCache.java | 189 +++++++++++------- 1 file changed, 122 insertions(+), 67 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCache.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCache.java index 6116f5c1eb44..39248912380a 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCache.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCache.java @@ -17,7 +17,6 @@ import io.netty.channel.EventLoop; import io.netty.handler.codec.dns.DnsRecord; -import io.netty.util.concurrent.ScheduledFuture; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import io.netty.util.internal.UnstableApi; @@ -29,8 +28,11 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Delayed; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import static io.netty.util.internal.ObjectUtil.checkNotNull; import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; @@ -41,12 +43,55 @@ */ @UnstableApi public class DefaultDnsCache implements DnsCache { - - private final ConcurrentMap resolveCache = PlatformDependent.newConcurrentHashMap(); - // Two years are supported by all our EventLoop implementations and so safe to use as maximum. // See also: https://github.com/netty/netty/commit/b47fb817991b42ec8808c7d26538f3f2464e1fa6 private static final int MAX_SUPPORTED_TTL_SECS = (int) TimeUnit.DAYS.toSeconds(365 * 2); + + private static final ScheduledFuture CANCELLED = new ScheduledFuture() { + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public long getDelay(TimeUnit unit) { + // We ignore unit and always return the minimum value to ensure the TTL of the cancelled marker is + // the smallest. + return Long.MIN_VALUE; + } + + @Override + public int compareTo(Delayed o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isCancelled() { + return true; + } + + @Override + public boolean isDone() { + return true; + } + + @Override + public Object get() { + throw new UnsupportedOperationException(); + } + + @Override + public Object get(long timeout, TimeUnit unit) { + throw new UnsupportedOperationException(); + } + }; + + private static final AtomicReferenceFieldUpdater FUTURE_UPDATER = + AtomicReferenceFieldUpdater.newUpdater( + DefaultDnsCache.Entries.class, ScheduledFuture.class, "expirationFuture"); + + private final ConcurrentMap resolveCache = PlatformDependent.newConcurrentHashMap(); private final int minTtl; private final int maxTtl; private final int negativeTtl; @@ -146,7 +191,7 @@ public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, return e; } cache0(appendDot(hostname), e, - Math.max(minTtl, Math.min(MAX_SUPPORTED_TTL_SECS, (int) Math.min(maxTtl, originalTtl))), loop); + Math.max(minTtl, (int) Math.min(maxTtl, originalTtl)), loop); return e; } @@ -161,45 +206,20 @@ public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, Throwable c return e; } - cache0(appendDot(hostname), e, Math.min(MAX_SUPPORTED_TTL_SECS, negativeTtl), loop); + cache0(appendDot(hostname), e, negativeTtl, loop); return e; } private void cache0(String hostname, DefaultDnsCacheEntry e, int ttl, EventLoop loop) { Entries entries = resolveCache.get(hostname); if (entries == null) { - entries = new Entries(e); + entries = new Entries(hostname); Entries oldEntries = resolveCache.putIfAbsent(hostname, entries); if (oldEntries != null) { entries = oldEntries; } } - entries.add(e); - - scheduleCacheExpiration(hostname, e, ttl, loop); - } - - private void scheduleCacheExpiration(final String hostname, final DefaultDnsCacheEntry e, - int ttl, - EventLoop loop) { - e.scheduleExpiration(loop, new Runnable() { - @Override - public void run() { - // We always remove all entries for a hostname once one entry expire. This is not the - // most efficient to do but this way we can guarantee that if a DnsResolver - // be configured to prefer one ip family over the other we will not return unexpected - // results to the enduser if one of the A or AAAA records has different TTL settings. - // - // As a TTL is just a hint of the maximum time a cache is allowed to cache stuff it's - // completely fine to remove the entry even if the TTL is not reached yet. - // - // See https://github.com/netty/netty/issues/7329 - Entries entries = resolveCache.remove(hostname); - if (entries != null) { - entries.clearAndCancel(); - } - } - }, ttl, TimeUnit.SECONDS); + entries.add(e, ttl, loop); } @Override @@ -217,7 +237,6 @@ private static final class DefaultDnsCacheEntry implements DnsCacheEntry { private final String hostname; private final InetAddress address; private final Throwable cause; - private volatile ScheduledFuture expirationFuture; DefaultDnsCacheEntry(String hostname, InetAddress address) { this.hostname = checkNotNull(hostname, "hostname"); @@ -241,16 +260,8 @@ public Throwable cause() { return cause; } - void scheduleExpiration(EventLoop loop, Runnable task, long delay, TimeUnit unit) { - assert expirationFuture == null : "expiration task scheduled already"; - expirationFuture = loop.schedule(task, delay, unit); - } - - void cancelExpiration() { - ScheduledFuture expirationFuture = this.expirationFuture; - if (expirationFuture != null) { - expirationFuture.cancel(false); - } + String hostname() { + return hostname; } @Override @@ -264,13 +275,18 @@ public String toString() { } // Directly extend AtomicReference for intrinsics and also to keep memory overhead low. - private static final class Entries extends AtomicReference> { + private final class Entries extends AtomicReference> implements Runnable { - Entries(DefaultDnsCacheEntry entry) { - super(Collections.singletonList(entry)); + private final String hostname; + // Needs to be package-private to be able to access it via the AtomicReferenceFieldUpdater + volatile ScheduledFuture expirationFuture; + + Entries(String hostname) { + super(Collections.emptyList()); + this.hostname = hostname; } - void add(DefaultDnsCacheEntry e) { + void add(DefaultDnsCacheEntry e, int ttl, EventLoop loop) { if (e.cause() == null) { for (;;) { List entries = get(); @@ -278,8 +294,9 @@ void add(DefaultDnsCacheEntry e) { final DefaultDnsCacheEntry firstEntry = entries.get(0); if (firstEntry.cause() != null) { assert entries.size() == 1; + if (compareAndSet(entries, Collections.singletonList(e))) { - firstEntry.cancelExpiration(); + scheduleCacheExpirationIfNeeded(ttl, loop); return; } else { // Need to try again as CAS failed @@ -289,33 +306,58 @@ void add(DefaultDnsCacheEntry e) { // Create a new List for COW semantics List newEntries = new ArrayList(entries.size() + 1); - DefaultDnsCacheEntry replacedEntry = null; - for (int i = 0; i < entries.size(); i++) { + int i = 0; + do { DefaultDnsCacheEntry entry = entries.get(i); // Only add old entry if the address is not the same as the one we try to add as well. // In this case we will skip it and just add the new entry as this may have // more up-to-date data and cancel the old after we were able to update the cache. if (!e.address().equals(entry.address())) { newEntries.add(entry); - } else { - assert replacedEntry == null; - replacedEntry = entry; } - } + } while (++i < entries.size()); newEntries.add(e); - if (compareAndSet(entries, newEntries)) { - if (replacedEntry != null) { - replacedEntry.cancelExpiration(); - } + if (compareAndSet(entries, Collections.unmodifiableList(newEntries))) { + scheduleCacheExpirationIfNeeded(ttl, loop); return; } } else if (compareAndSet(entries, Collections.singletonList(e))) { + scheduleCacheExpirationIfNeeded(ttl, loop); return; } } } else { - List entries = getAndSet(Collections.singletonList(e)); - cancelExpiration(entries); + set(Collections.singletonList(e)); + scheduleCacheExpirationIfNeeded(ttl, loop); + } + } + + private void scheduleCacheExpirationIfNeeded(int ttl, EventLoop loop) { + for (;;) { + // We currently don't calculate a new TTL when we need to retry the CAS as we don't expect this to + // be invoked very concurrently and also we use SECONDS anyway. If this ever becomes a problem + // we can reconsider. + ScheduledFuture oldFuture = FUTURE_UPDATER.get(this); + if (oldFuture == null || oldFuture.getDelay(TimeUnit.SECONDS) > ttl) { + ScheduledFuture newFuture = loop.schedule(this, ttl, TimeUnit.SECONDS); + // It is possible that + // 1. task will fire in between this line, or + // 2. multiple timers may be set if there is concurrency + // (1) Shouldn't be a problem because we will fail the CAS and then the next loop will see CANCELLED + // so the ttl will not be less, and we will bail out of the loop. + // (2) This is a trade-off to avoid concurrency resulting in contention on a synchronized block. + if (FUTURE_UPDATER.compareAndSet(this, oldFuture, newFuture)) { + if (oldFuture != null) { + oldFuture.cancel(true); + } + break; + } else { + // There was something else scheduled in the meantime... Cancel and try again. + newFuture.cancel(true); + } + } else { + break; + } } } @@ -325,15 +367,28 @@ boolean clearAndCancel() { return false; } - cancelExpiration(entries); + ScheduledFuture expirationFuture = FUTURE_UPDATER.getAndSet(this, CANCELLED); + if (expirationFuture != null) { + expirationFuture.cancel(false); + } + return true; } - private static void cancelExpiration(List entryList) { - final int numEntries = entryList.size(); - for (int i = 0; i < numEntries; i++) { - entryList.get(i).cancelExpiration(); - } + @Override + public void run() { + // We always remove all entries for a hostname once one entry expire. This is not the + // most efficient to do but this way we can guarantee that if a DnsResolver + // be configured to prefer one ip family over the other we will not return unexpected + // results to the enduser if one of the A or AAAA records has different TTL settings. + // + // As a TTL is just a hint of the maximum time a cache is allowed to cache stuff it's + // completely fine to remove the entry even if the TTL is not reached yet. + // + // See https://github.com/netty/netty/issues/7329 + resolveCache.remove(hostname, this); + + clearAndCancel(); } } From 8255f85f24edab5316c0db3aa55bb9709448daa5 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 15 Aug 2018 20:07:56 +0200 Subject: [PATCH 122/417] Rename SslHandler.close(...) to closeOutbound(...) as it is still useful and delegate to the methods. (#8193) * Rename SslHandler.close(...) to closeOutbound(...) as it is still useful and delegate to the methods. Motivation: Sometimes the user may want to send a close_notify without closing the underlying Channel. For this we offered the SslHandler.close(...) methods which were marked as deeprecated. We should offer an way to still do this without the user calling deprecated methods. See https://stackoverflow.com/questions/51710231/using-nettys-sslhandlerclosechannelhandlercontext-channelpromise/51753742#comment90555949_51753742 . Modifications: - Remove deprecation of the SslHandler.close(...) method that exactly allows this and rename these to closeOutbound(...) as this is more clear. - Add close(...) methods that delegate to these and mark these as deprecated. Result: Be able to send close_notify without closing the Channel. --- .../java/io/netty/handler/ssl/SslHandler.java | 72 ++++++++++++------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index fb6fc92766ad..7358d4230776 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -93,8 +93,8 @@ * *

Closing the session

*

- * To close the SSL session, the {@link #close()} method should be - * called to send the {@code close_notify} message to the remote peer. One + * To close the SSL session, the {@link #closeOutbound()} method should be + * called to send the {@code close_notify} message to the remote peer. One * exception is when you close the {@link Channel} - {@link SslHandler} * intercepts the close request and send the {@code close_notify} message * before the channel closure automatically. Once the SSL session is closed, @@ -622,42 +622,64 @@ public Future handshakeFuture() { } /** - * Sends an SSL {@code close_notify} message to the specified channel and - * destroys the underlying {@link SSLEngine}. - * - * @deprecated use {@link Channel#close()} or {@link ChannelHandlerContext#close()} + * Use {@link #closeOutbound()} */ @Deprecated public ChannelFuture close() { - return close(ctx.newPromise()); + return closeOutbound(); } /** - * See {@link #close()} - * - * @deprecated use {@link Channel#close()} or {@link ChannelHandlerContext#close()} + * Use {@link #closeOutbound(ChannelPromise)} */ @Deprecated - public ChannelFuture close(final ChannelPromise promise) { + public ChannelFuture close(ChannelPromise promise) { + return closeOutbound(promise); + } + + /** + * Sends an SSL {@code close_notify} message to the specified channel and + * destroys the underlying {@link SSLEngine}. This will not close the underlying + * {@link Channel}. If you want to also close the {@link Channel} use {@link Channel#close()} or + * {@link ChannelHandlerContext#close()} + */ + public ChannelFuture closeOutbound() { + return closeOutbound(ctx.newPromise()); + } + + /** + * Sends an SSL {@code close_notify} message to the specified channel and + * destroys the underlying {@link SSLEngine}. This will not close the underlying + * {@link Channel}. If you want to also close the {@link Channel} use {@link Channel#close()} or + * {@link ChannelHandlerContext#close()} + */ + public ChannelFuture closeOutbound(final ChannelPromise promise) { final ChannelHandlerContext ctx = this.ctx; - ctx.executor().execute(new Runnable() { - @Override - public void run() { - outboundClosed = true; - engine.closeOutbound(); - try { - flush(ctx, promise); - } catch (Exception e) { - if (!promise.tryFailure(e)) { - logger.warn("{} flush() raised a masked exception.", ctx.channel(), e); - } + if (ctx.executor().inEventLoop()) { + closeOutbound0(promise); + } else { + ctx.executor().execute(new Runnable() { + @Override + public void run() { + closeOutbound0(promise); } - } - }); - + }); + } return promise; } + private void closeOutbound0(ChannelPromise promise) { + outboundClosed = true; + engine.closeOutbound(); + try { + flush(ctx, promise); + } catch (Exception e) { + if (!promise.tryFailure(e)) { + logger.warn("{} flush() raised a masked exception.", ctx.channel(), e); + } + } + } + /** * Return the {@link Future} that will get notified if the inbound of the {@link SSLEngine} is closed. * From bbe2e4d224f839fbe9203bafc16eadf278d76cec Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 18 Aug 2018 06:26:45 +0200 Subject: [PATCH 123/417] We should try to load netty-tcnative before using it in OpenSslCertificateException. (#8202) Motivation: In OpenSslCertificateException we should ensure we try to load netty-tcnative before trying to use any class from it as otherwise it may throw an error due missing linking of the native libs. Modifications: - Ensure we call OpenSsl.isAvailable() before we try to use netty-tcnative for validation - Add testcase. Result: No more errors causing by not loading native libs before trying to use these. --- .../handler/ssl/OpenSslCertificateException.java | 4 +++- .../ssl/OpenSslCertificateExceptionTest.java | 14 ++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java index 4672d00787b9..f20b2d3ba08e 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java @@ -70,7 +70,9 @@ public int errorCode() { } private static int checkErrorCode(int errorCode) { - if (!CertificateVerifier.isValid(errorCode)) { + // Call OpenSsl.isAvailable() to ensure we try to load the native lib as CertificateVerifier.isValid(...) + // will depend on it. If loading fails we will just skip the validation. + if (OpenSsl.isAvailable() && !CertificateVerifier.isValid(errorCode)) { throw new IllegalArgumentException("errorCode '" + errorCode + "' invalid, see https://www.openssl.org/docs/man1.0.2/apps/verify.html."); } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslCertificateExceptionTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslCertificateExceptionTest.java index 229e853cd203..c9e8163fa66d 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslCertificateExceptionTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslCertificateExceptionTest.java @@ -18,20 +18,15 @@ import io.netty.internal.tcnative.CertificateVerifier; import org.junit.Assert; import org.junit.Assume; -import org.junit.BeforeClass; import org.junit.Test; import java.lang.reflect.Field; public class OpenSslCertificateExceptionTest { - @BeforeClass - public static void assumeOpenSsl() { - Assume.assumeTrue(OpenSsl.isAvailable()); - } - @Test public void testValidErrorCode() throws Exception { + Assume.assumeTrue(OpenSsl.isAvailable()); Field[] fields = CertificateVerifier.class.getFields(); for (Field field : fields) { if (field.isAccessible()) { @@ -44,6 +39,13 @@ public void testValidErrorCode() throws Exception { @Test(expected = IllegalArgumentException.class) public void testNonValidErrorCode() { + Assume.assumeTrue(OpenSsl.isAvailable()); new OpenSslCertificateException(Integer.MIN_VALUE); } + + @Test + public void testCanBeInstancedWhenOpenSslIsNotAvailable() { + Assume.assumeFalse(OpenSsl.isAvailable()); + new OpenSslCertificateException(0); + } } From df00539fa22b2162b4203d43bbc5abc3ac08924e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 18 Aug 2018 07:20:44 +0200 Subject: [PATCH 124/417] Allow to load PrivateKey via OpenSSL Engine (#8200) Motivation: OpenSSL itself has an abstraction which allows you to customize some things. For example it is possible to load the PrivateKey from the engine. We should support this. Modifications: Add two new static methods to OpenSslX509KeyManagerFactory which allow to create an OpenSslX509KeyManagerFactory that loads its PrivateKey via the OpenSSL Engine directly. Result: More flexible usage of OpenSSL possible --- .../ssl/DefaultOpenSslKeyMaterial.java | 117 +++++++++++ .../netty/handler/ssl/OpenSslKeyMaterial.java | 92 +-------- .../ssl/OpenSslKeyMaterialProvider.java | 12 +- .../netty/handler/ssl/OpenSslPrivateKey.java | 190 ++++++++++++++++++ .../ssl/OpenSslX509KeyManagerFactory.java | 158 ++++++++++++++- .../java/io/netty/handler/ssl/SslContext.java | 5 +- pom.xml | 2 +- 7 files changed, 485 insertions(+), 91 deletions(-) create mode 100644 handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java create mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java diff --git a/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java b/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java new file mode 100644 index 000000000000..4bba0e741f95 --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java @@ -0,0 +1,117 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.internal.tcnative.SSL; +import io.netty.util.AbstractReferenceCounted; +import io.netty.util.IllegalReferenceCountException; +import io.netty.util.ResourceLeakDetector; +import io.netty.util.ResourceLeakDetectorFactory; +import io.netty.util.ResourceLeakTracker; + +final class DefaultOpenSslKeyMaterial extends AbstractReferenceCounted implements OpenSslKeyMaterial { + + private static final ResourceLeakDetector leakDetector = + ResourceLeakDetectorFactory.instance().newResourceLeakDetector(DefaultOpenSslKeyMaterial.class); + private final ResourceLeakTracker leak; + private long chain; + private long privateKey; + + DefaultOpenSslKeyMaterial(long chain, long privateKey) { + this.chain = chain; + this.privateKey = privateKey; + leak = leakDetector.track(this); + } + + @Override + public long certificateChainAddress() { + if (refCnt() <= 0) { + throw new IllegalReferenceCountException(); + } + return chain; + } + + @Override + public long privateKeyAddress() { + if (refCnt() <= 0) { + throw new IllegalReferenceCountException(); + } + return privateKey; + } + + @Override + protected void deallocate() { + SSL.freeX509Chain(chain); + chain = 0; + SSL.freePrivateKey(privateKey); + privateKey = 0; + if (leak != null) { + boolean closed = leak.close(this); + assert closed; + } + } + + @Override + public DefaultOpenSslKeyMaterial retain() { + if (leak != null) { + leak.record(); + } + super.retain(); + return this; + } + + @Override + public DefaultOpenSslKeyMaterial retain(int increment) { + if (leak != null) { + leak.record(); + } + super.retain(increment); + return this; + } + + @Override + public DefaultOpenSslKeyMaterial touch() { + if (leak != null) { + leak.record(); + } + super.touch(); + return this; + } + + @Override + public DefaultOpenSslKeyMaterial touch(Object hint) { + if (leak != null) { + leak.record(hint); + } + return this; + } + + @Override + public boolean release() { + if (leak != null) { + leak.record(); + } + return super.release(); + } + + @Override + public boolean release(int decrement) { + if (leak != null) { + leak.record(); + } + return super.release(decrement); + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java index 749026807bd5..29099e5fa179 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java @@ -15,110 +15,38 @@ */ package io.netty.handler.ssl; -import io.netty.internal.tcnative.SSL; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakDetectorFactory; -import io.netty.util.ResourceLeakTracker; +import io.netty.util.ReferenceCounted; /** * Holds references to the native key-material that is used by OpenSSL. */ -final class OpenSslKeyMaterial extends AbstractReferenceCounted { - - private static final ResourceLeakDetector leakDetector = - ResourceLeakDetectorFactory.instance().newResourceLeakDetector(OpenSslKeyMaterial.class); - private final ResourceLeakTracker leak; - private long chain; - private long privateKey; - - OpenSslKeyMaterial(long chain, long privateKey) { - this.chain = chain; - this.privateKey = privateKey; - leak = leakDetector.track(this); - } +interface OpenSslKeyMaterial extends ReferenceCounted { /** * Returns the pointer to the {@code STACK_OF(X509)} which holds the certificate chain. */ - public long certificateChainAddress() { - if (refCnt() <= 0) { - throw new IllegalReferenceCountException(); - } - return chain; - } + long certificateChainAddress(); /** * Returns the pointer to the {@code EVP_PKEY}. */ - public long privateKeyAddress() { - if (refCnt() <= 0) { - throw new IllegalReferenceCountException(); - } - return privateKey; - } - - @Override - protected void deallocate() { - SSL.freeX509Chain(chain); - chain = 0; - SSL.freePrivateKey(privateKey); - privateKey = 0; - if (leak != null) { - boolean closed = leak.close(this); - assert closed; - } - } + long privateKeyAddress(); @Override - public OpenSslKeyMaterial retain() { - if (leak != null) { - leak.record(); - } - super.retain(); - return this; - } + OpenSslKeyMaterial retain(); @Override - public OpenSslKeyMaterial retain(int increment) { - if (leak != null) { - leak.record(); - } - super.retain(increment); - return this; - } + OpenSslKeyMaterial retain(int increment); @Override - public OpenSslKeyMaterial touch() { - if (leak != null) { - leak.record(); - } - super.touch(); - return this; - } + OpenSslKeyMaterial touch(); @Override - public OpenSslKeyMaterial touch(Object hint) { - if (leak != null) { - leak.record(hint); - } - return this; - } + OpenSslKeyMaterial touch(Object hint); @Override - public boolean release() { - if (leak != null) { - leak.record(); - } - return super.release(); - } + boolean release(); @Override - public boolean release(int decrement) { - if (leak != null) { - leak.record(); - } - return super.release(decrement); - } + boolean release(int decrement); } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java index 9f4d6ee2fbf9..7430b77f7d7e 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java @@ -62,10 +62,16 @@ OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) t long pkey = 0; try { chainBio = toBIO(allocator, encoded.retain()); - pkeyBio = toBIO(allocator, key); chain = SSL.parseX509Chain(chainBio); - pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password); - OpenSslKeyMaterial keyMaterial = new OpenSslKeyMaterial(chain, pkey); + + OpenSslKeyMaterial keyMaterial; + if (key instanceof OpenSslPrivateKey) { + keyMaterial = ((OpenSslPrivateKey) key).toKeyMaterial(chain); + } else { + pkeyBio = toBIO(allocator, key); + pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password); + keyMaterial = new DefaultOpenSslKeyMaterial(chain, pkey); + } // See the chain and pkey to 0 so we will not release it as the ownership was // transferred to OpenSslKeyMaterial. diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java new file mode 100644 index 000000000000..de1ff04daf1c --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java @@ -0,0 +1,190 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.internal.tcnative.SSL; +import io.netty.util.AbstractReferenceCounted; +import io.netty.util.IllegalReferenceCountException; + +import javax.security.auth.Destroyable; +import java.security.PrivateKey; + +final class OpenSslPrivateKey extends AbstractReferenceCounted implements PrivateKey { + + private long privateKeyAddress; + + OpenSslPrivateKey(long privateKeyAddress) { + this.privateKeyAddress = privateKeyAddress; + } + + @Override + public String getAlgorithm() { + return "unkown"; + } + + @Override + public String getFormat() { + // As we do not support encoding we should return null as stated in the javadocs of PrivateKey. + return null; + } + + @Override + public byte[] getEncoded() { + return null; + } + + /** + * Returns the pointer to the {@code EVP_PKEY}. + */ + long privateKeyAddress() { + if (refCnt() <= 0) { + throw new IllegalReferenceCountException(); + } + return privateKeyAddress; + } + + @Override + protected void deallocate() { + SSL.freePrivateKey(privateKeyAddress); + privateKeyAddress = 0; + } + + @Override + public OpenSslPrivateKey retain() { + super.retain(); + return this; + } + + @Override + public OpenSslPrivateKey retain(int increment) { + super.retain(increment); + return this; + } + + @Override + public OpenSslPrivateKey touch() { + super.touch(); + return this; + } + + @Override + public OpenSslPrivateKey touch(Object hint) { + return this; + } + + /** + * NOTE: This is a JDK8 interface/method. Due to backwards compatibility + * reasons it's not possible to slap the {@code @Override} annotation onto + * this method. + * + * @see Destroyable#destroy() + */ + public void destroy() { + release(refCnt()); + } + + /** + * NOTE: This is a JDK8 interface/method. Due to backwards compatibility + * reasons it's not possible to slap the {@code @Override} annotation onto + * this method. + * + * @see Destroyable#isDestroyed() + */ + public boolean isDestroyed() { + return refCnt() == 0; + } + + /** + * Convert to a {@link OpenSslKeyMaterial}. Reference count of both is shared. + */ + OpenSslKeyMaterial toKeyMaterial(long certificateChain) { + return new OpenSslPrivateKeyMaterial(certificateChain); + } + + private final class OpenSslPrivateKeyMaterial implements OpenSslKeyMaterial { + + private long certificateChain; + + OpenSslPrivateKeyMaterial(long certificateChain) { + this.certificateChain = certificateChain; + } + + @Override + public long certificateChainAddress() { + if (refCnt() <= 0) { + throw new IllegalReferenceCountException(); + } + return certificateChain; + } + + @Override + public long privateKeyAddress() { + return OpenSslPrivateKey.this.privateKeyAddress(); + } + + @Override + public OpenSslKeyMaterial retain() { + OpenSslPrivateKey.this.retain(); + return this; + } + + @Override + public OpenSslKeyMaterial retain(int increment) { + OpenSslPrivateKey.this.retain(increment); + return this; + } + + @Override + public OpenSslKeyMaterial touch() { + OpenSslPrivateKey.this.touch(); + return this; + } + + @Override + public OpenSslKeyMaterial touch(Object hint) { + OpenSslPrivateKey.this.touch(hint); + return this; + } + + @Override + public boolean release() { + if (OpenSslPrivateKey.this.release()) { + releaseChain(); + return true; + } + return false; + } + + @Override + public boolean release(int decrement) { + if (OpenSslPrivateKey.this.release(decrement)) { + releaseChain(); + return true; + } + return false; + } + + private void releaseChain() { + SSL.freeX509Chain(certificateChain); + certificateChain = 0; + } + + @Override + public int refCnt() { + return OpenSslPrivateKey.this.refCnt(); + } + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java index e47625cabc9d..90d94cb656cc 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java @@ -17,6 +17,7 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.internal.tcnative.SSL; import io.netty.util.ReferenceCountUtil; import io.netty.util.internal.ObjectUtil; @@ -25,13 +26,25 @@ import javax.net.ssl.KeyManagerFactorySpi; import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.X509KeyManager; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.security.InvalidAlgorithmParameterException; +import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.KeyStoreSpi; import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; import java.security.Provider; import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @@ -108,9 +121,8 @@ protected synchronized void engineInit(KeyStore keyStore, char[] chars) } kmf.init(keyStore, chars); - providerFactory = new ProviderFactory( - ReferenceCountedOpenSslContext.chooseX509KeyManager(kmf.getKeyManagers()), - password(chars), Collections.list(keyStore.aliases())); + providerFactory = new ProviderFactory(ReferenceCountedOpenSslContext.chooseX509KeyManager( + kmf.getKeyManagers()), password(chars), Collections.list(keyStore.aliases())); } private static String password(char[] password) { @@ -218,4 +230,144 @@ void destroy() { } } } + + /** + * Create a new initialized {@link OpenSslX509KeyManagerFactory} which loads its {@link PrivateKey} directly from + * an {@code OpenSSL engine} via the + * ENGINE_load_private_key + * function. + */ + public static OpenSslX509KeyManagerFactory newEngineBased(File certificateChain, String password) + throws CertificateException, IOException, + KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { + return newEngineBased(SslContext.toX509Certificates(certificateChain), password); + } + + /** + * Create a new initialized {@link OpenSslX509KeyManagerFactory} which loads its {@link PrivateKey} directly from + * an {@code OpenSSL engine} via the + * ENGINE_load_private_key + * function. + */ + public static OpenSslX509KeyManagerFactory newEngineBased(X509Certificate[] certificateChain, String password) + throws CertificateException, IOException, + KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { + KeyStore store = new OpenSslEngineKeyStore(certificateChain.clone()); + store.load(null, null); + OpenSslX509KeyManagerFactory factory = new OpenSslX509KeyManagerFactory(); + factory.init(store, password == null ? null : password.toCharArray()); + return factory; + } + + private static final class OpenSslEngineKeyStore extends KeyStore { + private OpenSslEngineKeyStore(final X509Certificate[] certificateChain) { + super(new KeyStoreSpi() { + + private final Date creationDate = new Date(); + + @Override + public Key engineGetKey(String alias, char[] password) throws UnrecoverableKeyException { + if (engineContainsAlias(alias)) { + try { + return new OpenSslPrivateKey(SSL.loadPrivateKeyFromEngine( + alias, password == null ? null : new String(password))); + } catch (Exception e) { + UnrecoverableKeyException keyException = + new UnrecoverableKeyException("Unable to load key from engine"); + keyException.initCause(e); + throw keyException; + } + } + return null; + } + + @Override + public Certificate[] engineGetCertificateChain(String alias) { + return engineContainsAlias(alias)? certificateChain.clone() : null; + } + + @Override + public Certificate engineGetCertificate(String alias) { + return engineContainsAlias(alias)? certificateChain[0] : null; + } + + @Override + public Date engineGetCreationDate(String alias) { + return engineContainsAlias(alias)? creationDate : null; + } + + @Override + public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) + throws KeyStoreException { + throw new KeyStoreException("Not supported"); + } + + @Override + public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException { + throw new KeyStoreException("Not supported"); + } + + @Override + public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { + throw new KeyStoreException("Not supported"); + } + + @Override + public void engineDeleteEntry(String alias) throws KeyStoreException { + throw new KeyStoreException("Not supported"); + } + + @Override + public Enumeration engineAliases() { + return Collections.enumeration(Collections.singleton(SslContext.ALIAS)); + } + + @Override + public boolean engineContainsAlias(String alias) { + return SslContext.ALIAS.equals(alias); + } + + @Override + public int engineSize() { + return 1; + } + + @Override + public boolean engineIsKeyEntry(String alias) { + return engineContainsAlias(alias); + } + + @Override + public boolean engineIsCertificateEntry(String alias) { + return engineContainsAlias(alias); + } + + @Override + public String engineGetCertificateAlias(Certificate cert) { + if (cert instanceof X509Certificate) { + for (X509Certificate x509Certificate : certificateChain) { + if (x509Certificate.equals(cert)) { + return SslContext.ALIAS; + } + } + } + return null; + } + + @Override + public void engineStore(OutputStream stream, char[] password) { + throw new UnsupportedOperationException(); + } + + @Override + public void engineLoad(InputStream stream, char[] password) { + if (stream != null && password != null) { + throw new UnsupportedOperationException(); + } + } + }, null, "native"); + + OpenSsl.ensureAvailability(); + } + } } diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContext.java b/handler/src/main/java/io/netty/handler/ssl/SslContext.java index c842a1461552..08fbda8d1f48 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java @@ -53,7 +53,6 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; -import java.security.Security; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; @@ -85,6 +84,8 @@ * */ public abstract class SslContext { + static final String ALIAS = "key"; + static final CertificateFactory X509_CERT_FACTORY; static { try { @@ -1000,7 +1001,7 @@ static KeyStore buildKeyStore(X509Certificate[] certChain, PrivateKey key, char[ CertificateException, IOException { KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(null, null); - ks.setKeyEntry("key", key, keyPasswordChars, certChain); + ks.setKeyEntry(ALIAS, key, keyPasswordChars, certChain); return ks; } diff --git a/pom.xml b/pom.xml index 6eb819cfeafa..eaa1ea7fed87 100644 --- a/pom.xml +++ b/pom.xml @@ -221,7 +221,7 @@ fedora netty-tcnative - 2.0.12.Final + 2.0.13.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber From 785473788f3f19531294802b727fb10a48938222 Mon Sep 17 00:00:00 2001 From: Ziyan Mo Date: Fri, 17 Aug 2018 22:28:31 -0700 Subject: [PATCH 125/417] (Nio|Epoll)EventLoop.pendingTasks does not need to dispatch to the EventLoop (#8197) Motivation: EventLoop.pendingTasks should be (reasonably) cheap to invoke so it can be used within observability. Modifications: Remove code that dispatch access to the internal taskqueue to the EventLoop when invoked as this is not needed anymore with the current MPSC queues we are using. See https://github.com/netty/netty/issues/8196#issuecomment-413653286. Result: Fixes https://github.com/netty/netty/issues/8196 --- .../concurrent/SingleThreadEventExecutor.java | 2 +- .../io/netty/channel/epoll/EpollEventLoop.java | 17 ----------------- .../netty/channel/kqueue/KQueueEventLoop.java | 14 -------------- .../io/netty/channel/nio/NioEventLoop.java | 18 ------------------ 4 files changed, 1 insertion(+), 50 deletions(-) diff --git a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java index bb51d8f39629..be5814f5955e 100644 --- a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java +++ b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java @@ -304,7 +304,7 @@ protected boolean hasTasks() { * Return the number of tasks that are pending for processing. * * Be aware that this operation may be expensive as it depends on the internal implementation of the - * SingleThreadEventExecutor. So use it was care! + * SingleThreadEventExecutor. So use it with care! */ public int pendingTasks() { return taskQueue.size(); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index 68e9c125a18e..c90a2ca1069e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -73,12 +73,6 @@ public int get() throws Exception { return epollWaitNow(); } }; - private final Callable pendingTasksCallable = new Callable() { - @Override - public Integer call() throws Exception { - return EpollEventLoop.super.pendingTasks(); - } - }; private volatile int wakenUp; private volatile int ioRatio = 50; @@ -215,17 +209,6 @@ protected Queue newTaskQueue(int maxPendingTasks) { : PlatformDependent.newMpscQueue(maxPendingTasks); } - @Override - public int pendingTasks() { - // As we use a MpscQueue we need to ensure pendingTasks() is only executed from within the EventLoop as - // otherwise we may see unexpected behavior (as size() is only allowed to be called by a single consumer). - // See https://github.com/netty/netty/issues/5297 - if (inEventLoop()) { - return super.pendingTasks(); - } else { - return submit(pendingTasksCallable).syncUninterruptibly().getNow(); - } - } /** * Returns the percentage of the desired amount of time spent for I/O in the event loop. */ diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java index 5af59ba912c6..64a09c97d1bf 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java @@ -66,12 +66,6 @@ public int get() throws Exception { return kqueueWaitNow(); } }; - private final Callable pendingTasksCallable = new Callable() { - @Override - public Integer call() throws Exception { - return KQueueEventLoop.super.pendingTasks(); - } - }; private volatile int wakenUp; private volatile int ioRatio = 50; @@ -301,14 +295,6 @@ protected Queue newTaskQueue(int maxPendingTasks) { : PlatformDependent.newMpscQueue(maxPendingTasks); } - @Override - public int pendingTasks() { - // As we use a MpscQueue we need to ensure pendingTasks() is only executed from within the EventLoop as - // otherwise we may see unexpected behavior (as size() is only allowed to be called by a single consumer). - // See https://github.com/netty/netty/issues/5297 - return inEventLoop() ? super.pendingTasks() : submit(pendingTasksCallable).syncUninterruptibly().getNow(); - } - /** * Returns the percentage of the desired amount of time spent for I/O in the event loop. */ diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index 2c197775a921..e36e24a27978 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -71,12 +71,6 @@ public int get() throws Exception { return selectNow(); } }; - private final Callable pendingTasksCallable = new Callable() { - @Override - public Integer call() throws Exception { - return NioEventLoop.super.pendingTasks(); - } - }; // Workaround for JDK NIO bug. // @@ -259,18 +253,6 @@ protected Queue newTaskQueue(int maxPendingTasks) { : PlatformDependent.newMpscQueue(maxPendingTasks); } - @Override - public int pendingTasks() { - // As we use a MpscQueue we need to ensure pendingTasks() is only executed from within the EventLoop as - // otherwise we may see unexpected behavior (as size() is only allowed to be called by a single consumer). - // See https://github.com/netty/netty/issues/5297 - if (inEventLoop()) { - return super.pendingTasks(); - } else { - return submit(pendingTasksCallable).syncUninterruptibly().getNow(); - } - } - /** * Registers an arbitrary {@link SelectableChannel}, not necessarily created by Netty, to the {@link Selector} * of this event loop. Once the specified {@link SelectableChannel} is registered, the specified {@code task} will From 182ffdaf6d4c067514fc70b00c4a5abc67753531 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 18 Aug 2018 21:09:17 +0200 Subject: [PATCH 126/417] Only use manual safepoint polling in PlatformDependent0.copyMemory(...) when using java <= 8 (#8124) Motivation: Java9 and later does the safepoint polling by itself so there is not need for us to do it. Modifications: Check for java version before doing manual safepoint polling. Result: Less custom code and less overhead when using java9 and later. Fixes https://github.com/netty/netty/issues/8122. --- .../util/internal/PlatformDependent0.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java index c6ebb55131d7..53e9ed7bdcf2 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java @@ -550,7 +550,16 @@ static void putLong(byte[] data, int index, long value) { } static void copyMemory(long srcAddr, long dstAddr, long length) { - //UNSAFE.copyMemory(srcAddr, dstAddr, length); + // Manual safe-point polling is only needed prior Java9: + // See https://bugs.openjdk.java.net/browse/JDK-8149596 + if (javaVersion() <= 8) { + copyMemoryWithSafePointPolling(srcAddr, dstAddr, length); + } else { + UNSAFE.copyMemory(srcAddr, dstAddr, length); + } + } + + private static void copyMemoryWithSafePointPolling(long srcAddr, long dstAddr, long length) { while (length > 0) { long size = Math.min(length, UNSAFE_COPY_THRESHOLD); UNSAFE.copyMemory(srcAddr, dstAddr, size); @@ -561,7 +570,17 @@ static void copyMemory(long srcAddr, long dstAddr, long length) { } static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) { - //UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length); + // Manual safe-point polling is only needed prior Java9: + // See https://bugs.openjdk.java.net/browse/JDK-8149596 + if (javaVersion() <= 8) { + copyMemoryWithSafePointPolling(src, srcOffset, dst, dstOffset, length); + } else { + UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length); + } + } + + private static void copyMemoryWithSafePointPolling( + Object src, long srcOffset, Object dst, long dstOffset, long length) { while (length > 0) { long size = Math.min(length, UNSAFE_COPY_THRESHOLD); UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size); From ea4c315b45bf1bfa82cd75a7d3f38bff684e2ebc Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 21 Aug 2018 07:53:45 +0200 Subject: [PATCH 127/417] Ensure multiple shaded version of the same netty artifact can be loaded as long as the shaded prefix is different (#8207) Motivation: We should support to load multiple shaded versions of the same netty artifact as netty is often used in multiple dependencies. This is related to https://github.com/netty/netty/issues/7272. Modifications: - Use -fvisibility=hidden when compiling and use JNIEXPORT for things we really want to have exported - Ensure fields are declared as static so these are not exported - Adjust testsuite-shading to use install_name_tool on MacOS to change the id of the lib. Otherwise the wrong may be used. Result: Be able to use multiple shaded versions of the same netty artifact. --- testsuite-shading/pom.xml | 88 ++++++++++++++++++- .../io/netty/testsuite/shading/ShadingIT.java | 15 +++- transport-native-epoll/pom.xml | 4 +- .../src/main/c/netty_epoll_native.c | 40 +++++---- transport-native-kqueue/pom.xml | 2 +- .../src/main/c/netty_kqueue_eventarray.c | 2 +- .../src/main/c/netty_kqueue_native.c | 33 ++++--- transport-native-unix-common/pom.xml | 8 +- 8 files changed, 149 insertions(+), 43 deletions(-) diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index 5b42b7d66241..f2fdf5db29a5 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -34,6 +34,8 @@ ${project.build.directory}/classes-shaded ${classesShadedDir}/META-INF/native shaded + shaded2 + ${project.artifactId}-${project.version}.jar io.netty. @@ -104,6 +106,7 @@ maven-shade-plugin + shade package shade @@ -122,6 +125,26 @@ + + shade-1 + package + + shade + + + + + ${project.groupId} + + + + + ${shadedPackagePrefix} + ${shadingPrefix2}.${shadedPackagePrefix} + + + + @@ -141,8 +164,36 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -156,6 +207,7 @@ ${shadingPrefix} + ${shadingPrefix2} @@ -211,6 +263,7 @@ maven-shade-plugin + shade package shade @@ -229,6 +282,26 @@ + + shade-1 + package + + shade + + + + + ${project.groupId} + + + + + ${shadedPackagePrefix} + ${shadingPrefix2}.${shadedPackagePrefix} + + + + @@ -248,8 +321,14 @@ - - + + + + + + + + @@ -263,6 +342,7 @@ ${shadingPrefix} + ${shadingPrefix2} diff --git a/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java index 1606bfcc9c10..0f45fddef84e 100644 --- a/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java +++ b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java @@ -23,19 +23,26 @@ public class ShadingIT { + private static final String SHADING_PREFIX = System.getProperty("shadingPrefix2"); + private static final String SHADING_PREFIX2 = System.getProperty("shadingPrefix"); + @Test public void testShadingNativeTransport() throws Exception { - testShading0(PlatformDependent.isOsx() ? "io.netty.channel.kqueue.KQueue" : "io.netty.channel.epoll.Epoll"); + String className = PlatformDependent.isOsx() ? + "io.netty.channel.kqueue.KQueue" : "io.netty.channel.epoll.Epoll"; + testShading0(SHADING_PREFIX, className); + testShading0(SHADING_PREFIX2, className); } @Ignore("Figure out why this sometimes fail on the CI") @Test public void testShadingTcnative() throws Exception { - testShading0("io.netty.handler.ssl.OpenSsl"); + String className = "io.netty.handler.ssl.OpenSsl"; + testShading0(SHADING_PREFIX, className); + testShading0(SHADING_PREFIX2, className); } - private static void testShading0(String classname) throws Exception { - String shadingPrefix = System.getProperty("shadingPrefix"); + private static void testShading0(String shadingPrefix, String classname) throws Exception { final Class clazz = Class.forName(shadingPrefix + '.' + classname); Method method = clazz.getMethod("ensureAvailability"); method.invoke(null); diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 3fedacdad3b8..a67ae5fc2a91 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -265,7 +265,7 @@ ${linux.sendmmsg.support}${glibc.sendmmsg.support} .*IO_NETTY_SENDMSSG_NOT_FOUND.* - CFLAGS=-O3 -DIO_NETTY_SENDMMSG_NOT_FOUND -Werror -fno-omit-frame-pointer -Wunused-variable -I${unix.common.include.unpacked.dir} + CFLAGS=-O3 -DIO_NETTY_SENDMMSG_NOT_FOUND -Werror -fno-omit-frame-pointer -Wunused-variable -fvisibility=hidden -I${unix.common.include.unpacked.dir} false @@ -281,7 +281,7 @@ ${jni.compiler.args.cflags} ^((?!CFLAGS=).)*$ - CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable -I${unix.common.include.unpacked.dir} + CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable -fvisibility=hidden -I${unix.common.include.unpacked.dir} false diff --git a/transport-native-epoll/src/main/c/netty_epoll_native.c b/transport-native-epoll/src/main/c/netty_epoll_native.c index 0352844c7d9f..68ef0ed2c0aa 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_native.c +++ b/transport-native-epoll/src/main/c/netty_epoll_native.c @@ -65,11 +65,11 @@ struct mmsghdr { #endif // Those are initialized in the init(...) method and cached for performance reasons -jfieldID packetAddrFieldId = NULL; -jfieldID packetScopeIdFieldId = NULL; -jfieldID packetPortFieldId = NULL; -jfieldID packetMemoryAddressFieldId = NULL; -jfieldID packetCountFieldId = NULL; +static jfieldID packetAddrFieldId = NULL; +static jfieldID packetScopeIdFieldId = NULL; +static jfieldID packetPortFieldId = NULL; +static jfieldID packetMemoryAddressFieldId = NULL; +static jfieldID packetCountFieldId = NULL; // util methods static int getSysctlValue(const char * property, int* returnValue) { @@ -505,7 +505,7 @@ static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) { } // Invoked by the JVM when statically linked -jint JNI_OnLoad_netty_transport_native_epoll(JavaVM* vm, void* reserved) { +static jint JNI_OnLoad_netty_transport_native_epoll0(JavaVM* vm, void* reserved) { JNIEnv* env; if ((*vm)->GetEnv(vm, (void**) &env, NETTY_JNI_VERSION) != JNI_OK) { return JNI_ERR; @@ -536,14 +536,7 @@ jint JNI_OnLoad_netty_transport_native_epoll(JavaVM* vm, void* reserved) { return ret; } -#ifndef NETTY_BUILD_STATIC -JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { - return JNI_OnLoad_netty_transport_native_epoll(vm, reserved); -} -#endif /* NETTY_BUILD_STATIC */ - -// Invoked by the JVM when statically linked -void JNI_OnUnload_netty_transport_native_epoll(JavaVM* vm, void* reserved) { +static void JNI_OnUnload_netty_transport_native_epoll0(JavaVM* vm, void* reserved) { JNIEnv* env; if ((*vm)->GetEnv(vm, (void**) &env, NETTY_JNI_VERSION) != JNI_OK) { // Something is wrong but nothing we can do about this :( @@ -552,8 +545,25 @@ void JNI_OnUnload_netty_transport_native_epoll(JavaVM* vm, void* reserved) { netty_epoll_native_JNI_OnUnLoad(env); } +// We build with -fvisibility=hidden so ensure we mark everything that needs to be visible with JNIEXPORT +// http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-February/014549.html + +// Invoked by the JVM when statically linked +JNIEXPORT jint JNI_OnLoad_netty_transport_native_epoll(JavaVM* vm, void* reserved) { + return JNI_OnLoad_netty_transport_native_epoll0(vm, reserved); +} + +// Invoked by the JVM when statically linked +JNIEXPORT void JNI_OnUnload_netty_transport_native_epoll(JavaVM* vm, void* reserved) { + JNI_OnUnload_netty_transport_native_epoll0(vm, reserved); +} + #ifndef NETTY_BUILD_STATIC +JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + return JNI_OnLoad_netty_transport_native_epoll0(vm, reserved); +} + JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) { - JNI_OnUnload_netty_transport_native_epoll(vm, reserved); + JNI_OnUnload_netty_transport_native_epoll0(vm, reserved); } #endif /* NETTY_BUILD_STATIC */ diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 749a2036f85a..e8e38f562b42 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -361,7 +361,7 @@ ${project.build.directory}/unix-common-lib ${unix.common.lib.dir}/META-INF/native/lib ${unix.common.lib.dir}/META-INF/native/include - CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable -I${unix.common.include.unpacked.dir} + CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable -fvisibility=hidden -I${unix.common.include.unpacked.dir} LDFLAGS=-z now -L${unix.common.lib.unpacked.dir} -Wl,--whole-archive -l${unix.common.lib.name} -Wl,--no-whole-archive true diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c b/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c index ad7a20d6546a..0fb0c4cffd77 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c @@ -24,7 +24,7 @@ #include "netty_unix_jni.h" #include "netty_unix_util.h" -jfieldID kqueueJniPtrFieldId = NULL; +static jfieldID kqueueJniPtrFieldId = NULL; static void netty_kqueue_eventarray_evSet(JNIEnv* env, jclass clzz, jlong keventAddress, jobject channel, jint ident, jshort filter, jshort flags, jint fflags) { // Create a global pointer, cast it as a long, and retain it in java to re-use and free later. diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_native.c b/transport-native-kqueue/src/main/c/netty_kqueue_native.c index 94899e037e48..389569735e15 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_native.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_native.c @@ -65,7 +65,7 @@ #endif /* NOTE_DISCONNECTED */ #endif /* __APPLE__ */ -clockid_t waitClockId = 0; // initialized by netty_unix_util_initialize_wait_clock +static clockid_t waitClockId = 0; // initialized by netty_unix_util_initialize_wait_clock static jint netty_kqueue_native_kqueueCreate(JNIEnv* env, jclass clazz) { jint kq = kqueue(); @@ -318,8 +318,7 @@ static void netty_kqueue_native_JNI_OnUnLoad(JNIEnv* env) { netty_kqueue_eventarray_JNI_OnUnLoad(env); } -// Invoked by the JVM when statically linked -jint JNI_OnLoad_netty_transport_native_kqueue(JavaVM* vm, void* reserved) { +static jint JNI_OnLoad_netty_transport_native_kqueue0(JavaVM* vm, void* reserved) { JNIEnv* env; if ((*vm)->GetEnv(vm, (void**) &env, NETTY_JNI_VERSION) != JNI_OK) { return JNI_ERR; @@ -351,14 +350,7 @@ jint JNI_OnLoad_netty_transport_native_kqueue(JavaVM* vm, void* reserved) { return ret; } -#ifndef NETTY_BUILD_STATIC -JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { - return JNI_OnLoad_netty_transport_native_kqueue(vm, reserved); -} -#endif /* NETTY_BUILD_STATIC */ - -// Invoked by the JVM when statically linked -void JNI_OnUnload_netty_transport_native_kqueue(JavaVM* vm, void* reserved) { +static void JNI_OnUnload_netty_transport_native_kqueue0(JavaVM* vm, void* reserved) { JNIEnv* env; if ((*vm)->GetEnv(vm, (void**) &env, NETTY_JNI_VERSION) != JNI_OK) { // Something is wrong but nothing we can do about this :( @@ -367,8 +359,25 @@ void JNI_OnUnload_netty_transport_native_kqueue(JavaVM* vm, void* reserved) { netty_kqueue_native_JNI_OnUnLoad(env); } +// We build with -fvisibility=hidden so ensure we mark everything that needs to be visible with JNIEXPORT +// http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-February/014549.html + +// Invoked by the JVM when statically linked +JNIEXPORT jint JNI_OnLoad_netty_transport_native_kqueue(JavaVM* vm, void* reserved) { + return JNI_OnLoad_netty_transport_native_kqueue0(vm, reserved); +} + +// Invoked by the JVM when statically linked +JNIEXPORT void JNI_OnUnload_netty_transport_native_kqueue(JavaVM* vm, void* reserved) { + JNI_OnUnload_netty_transport_native_kqueue0(vm, reserved); +} + #ifndef NETTY_BUILD_STATIC +JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + return JNI_OnLoad_netty_transport_native_kqueue0(vm, reserved); +} + JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) { - return JNI_OnUnload_netty_transport_native_kqueue(vm, reserved); + return JNI_OnUnload_netty_transport_native_kqueue0(vm, reserved); } #endif /* NETTY_BUILD_STATIC */ diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index d08cfad494be..78d8c918d27f 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -101,7 +101,7 @@ - + - - - - - - - - - - - - - - - - From a0a4d87eab15e89a98dc3683a138cfe218807b65 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 24 Aug 2018 06:42:21 +0200 Subject: [PATCH 130/417] Update to netty-tcnative 2.0.14 which does correctly handle shading (#8218) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eaa1ea7fed87..a05662c354d6 100644 --- a/pom.xml +++ b/pom.xml @@ -221,7 +221,7 @@ fedora netty-tcnative - 2.0.13.Final + 2.0.14.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber From 3fc789e83fe3f380ad1936075287adeead328b58 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 24 Aug 2018 06:36:06 +0000 Subject: [PATCH 131/417] [maven-release-plugin] prepare release netty-4.1.29.Final --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 76 insertions(+), 72 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 19e371276d6b..0863f2ea3902 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-all diff --git a/bom/pom.xml b/bom/pom.xml index ca3aa4a20bea..6a39f22ad924 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.29.Final-SNAPSHOT + 4.1.29.Final pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.29.Final @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec-dns - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec-haproxy - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec-http - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec-http2 - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec-memcache - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec-mqtt - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec-redis - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec-smtp - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec-socks - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec-stomp - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-codec-xml - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-common - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-dev-tools - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-handler - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-handler-proxy - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-resolver - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-resolver-dns - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-transport - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-transport-rxtx - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-transport-sctp - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-transport-udt - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-example - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-all - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-transport-native-unix-common - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-transport-native-unix-common - 4.1.29.Final-SNAPSHOT + 4.1.29.Final linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.29.Final-SNAPSHOT + 4.1.29.Final osx-x86_64 io.netty netty-transport-native-epoll - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-transport-native-epoll - 4.1.29.Final-SNAPSHOT + 4.1.29.Final linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.29.Final-SNAPSHOT + 4.1.29.Final io.netty netty-transport-native-kqueue - 4.1.29.Final-SNAPSHOT + 4.1.29.Final osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 90811150e5bb..fe17e19241d1 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index ff32a88a3f7e..e5484aa6caa7 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index d82845e30d16..b62732af76a1 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 4d074f0ae923..7ae925e1181d 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 984886fa231a..097d3ea6c555 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index d4485edcb47f..30b0017c3809 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 5dda802bdcc6..eec0a9c2f15d 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 78876d583dab..64ec8a0d8ed7 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index fb309ceaf035..ea731da25a96 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 856546e31b9c..f2d166d8ba76 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index b19fbb8fac06..74503ac9bcea 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index a3f4e0540092..a33ff973cc3e 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 35cd0c3b898d..e7b6e05bb49a 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-codec diff --git a/common/pom.xml b/common/pom.xml index 6292c3653557..cc19be72e456 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 3d985746f3c4..35f80c5294d1 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.29.Final-SNAPSHOT + 4.1.29.Final Netty/Dev-Tools @@ -50,4 +50,8 @@ + + + netty-4.1.29.Final + diff --git a/example/pom.xml b/example/pom.xml index e4364db219e3..908bcdd52781 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index c4df9f610222..8653ed28bf27 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index 25f87786dce0..a5bda3ec18ec 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 536c52b073e5..b2330bc8c584 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-microbench diff --git a/pom.xml b/pom.xml index a05662c354d6..85b0b1bac407 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.29.Final-SNAPSHOT + 4.1.29.Final Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.29.Final diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index 67963f967668..b4754a21b2d7 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index eb369cc96c59..e3389dd0dffa 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index e29da6352cb9..37ff494074f7 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index c0e9cc35e866..56375734cfc9 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index dbe9ccfc9f81..a6c46be0997d 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 5b580fdfd51d..1db9ec5233fe 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index f07f5eaa7fe1..a6b051ae12a3 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 30cb691a81c6..8f4e3f832de7 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index a67ae5fc2a91..308bd4ab4487 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index e8e38f562b42..acbe02032fa8 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 9e3abfd3344d..3ac669bacb68 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 78d8c918d27f..190a7bbf6fa8 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index ee164d716b46..b8a5cc75b226 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index f7ff740db97a..dbda9aff082f 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index aacd8ad6e255..d2b0051890ee 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 70bc74f411fa..598250bdbb8d 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final-SNAPSHOT + 4.1.29.Final netty-transport From a580dc7585c674dea5d77df4caf90fffdb9390e8 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 24 Aug 2018 06:36:33 +0000 Subject: [PATCH 132/417] [maven-release-plugin] prepare for next development iteration --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 72 insertions(+), 76 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 0863f2ea3902..73d7d1de81a3 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 6a39f22ad924..2d085a50de22 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.29.Final + 4.1.30.Final-SNAPSHOT pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.29.Final + HEAD @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec-dns - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec-haproxy - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec-http - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec-http2 - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec-memcache - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec-mqtt - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec-redis - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec-smtp - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec-socks - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec-stomp - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-codec-xml - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-common - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-dev-tools - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-handler - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-handler-proxy - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-resolver - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-resolver-dns - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-transport - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-transport-rxtx - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-transport-sctp - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-transport-udt - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-example - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-all - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.29.Final + 4.1.30.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.29.Final + 4.1.30.Final-SNAPSHOT osx-x86_64 io.netty netty-transport-native-epoll - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-transport-native-epoll - 4.1.29.Final + 4.1.30.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.29.Final + 4.1.30.Final-SNAPSHOT io.netty netty-transport-native-kqueue - 4.1.29.Final + 4.1.30.Final-SNAPSHOT osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index fe17e19241d1..dff323d8b486 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index e5484aa6caa7..49aaecdadb32 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index b62732af76a1..2a9ca8e8f696 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 7ae925e1181d..2a519d1ed089 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 097d3ea6c555..beda9f717172 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 30b0017c3809..067f35db3d85 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index eec0a9c2f15d..6b27ad7d5fe5 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 64ec8a0d8ed7..5e5f883c72fa 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index ea731da25a96..23dc22904c3a 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index f2d166d8ba76..31fcfbdf4083 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 74503ac9bcea..67f5d804ef4f 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index a33ff973cc3e..54bbf043bbce 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index e7b6e05bb49a..2799b130995b 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-codec diff --git a/common/pom.xml b/common/pom.xml index cc19be72e456..e2022967a8a9 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 35f80c5294d1..34084e5d1559 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.29.Final + 4.1.30.Final-SNAPSHOT Netty/Dev-Tools @@ -50,8 +50,4 @@ - - - netty-4.1.29.Final - diff --git a/example/pom.xml b/example/pom.xml index 908bcdd52781..f9d0a6be83cf 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 8653ed28bf27..154fac180273 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index a5bda3ec18ec..ebfbf773c417 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index b2330bc8c584..441cd81246e5 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-microbench diff --git a/pom.xml b/pom.xml index 85b0b1bac407..f0389cdddd74 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.29.Final + 4.1.30.Final-SNAPSHOT Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.29.Final + HEAD diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index b4754a21b2d7..cbcecd3fdea3 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index e3389dd0dffa..583135a0f6da 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 37ff494074f7..9fdb78e9ceb0 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 56375734cfc9..430f4d10c521 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index a6c46be0997d..de6c30fdb334 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 1db9ec5233fe..0648979e1317 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index a6b051ae12a3..b125492b9439 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 8f4e3f832de7..8a0dd0d699a7 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 308bd4ab4487..01350b23b24f 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index acbe02032fa8..646f54dc5e70 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 3ac669bacb68..b27aae873a78 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 190a7bbf6fa8..3ac755f2c643 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index b8a5cc75b226..9beb7bbe301d 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index dbda9aff082f..1d0c3881f284 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index d2b0051890ee..10707eee04f1 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 598250bdbb8d..09b47e6d6e63 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.29.Final + 4.1.30.Final-SNAPSHOT netty-transport From 6888af6ba57ab6b88ad95a9383ee3c3492efec96 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 24 Aug 2018 19:48:27 +0200 Subject: [PATCH 133/417] Allow to parse hosts file which is stored in a different encoding then the default system encoding. (#8211) Motivation: We should support to parse and read a hosts file which is stored in a different encoding then the system default. Beside this when we are on windows we should just try to parse it with multiple different charset before giving up as there is no real standard what charset to use. Modifications: - Add more method overloads to HostsFileParser that take a Charset. - Try to parse with multiple Charsets in DefaultHostsFileEntriesResolver when windows is used. - Add unit test Result: Fixes https://github.com/netty/netty/issues/8208. --- .../DefaultHostsFileEntriesResolver.java | 15 +++++- .../io/netty/resolver/HostsFileParser.java | 47 +++++++++++++++--- .../netty/resolver/HostsFileParserTest.java | 42 ++++++++++++++++ .../resources/io/netty/resolver/hosts-unicode | Bin 0 -> 426 bytes 4 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 resolver/src/test/resources/io/netty/resolver/hosts-unicode diff --git a/resolver/src/main/java/io/netty/resolver/DefaultHostsFileEntriesResolver.java b/resolver/src/main/java/io/netty/resolver/DefaultHostsFileEntriesResolver.java index 7598a29200c6..9051262eb1c4 100644 --- a/resolver/src/main/java/io/netty/resolver/DefaultHostsFileEntriesResolver.java +++ b/resolver/src/main/java/io/netty/resolver/DefaultHostsFileEntriesResolver.java @@ -15,11 +15,14 @@ */ package io.netty.resolver; +import io.netty.util.CharsetUtil; +import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.UnstableApi; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.nio.charset.Charset; import java.util.Locale; import java.util.Map; @@ -33,7 +36,7 @@ public final class DefaultHostsFileEntriesResolver implements HostsFileEntriesRe private final Map inet6Entries; public DefaultHostsFileEntriesResolver() { - this(HostsFileParser.parseSilently()); + this(parseEntries()); } // for testing purpose only @@ -65,4 +68,14 @@ public InetAddress address(String inetHost, ResolvedAddressTypes resolvedAddress String normalize(String inetHost) { return inetHost.toLowerCase(Locale.ENGLISH); } + + private static HostsFileEntries parseEntries() { + if (PlatformDependent.isWindows()) { + // Ony windows there seems to be no standard for the encoding used for the hosts file, so let us + // try multiple until we either were able to parse it or there is none left and so we return an + // empty intstance. + return HostsFileParser.parseSilently(Charset.defaultCharset(), CharsetUtil.UTF_16, CharsetUtil.UTF_8); + } + return HostsFileParser.parseSilently(); + } } diff --git a/resolver/src/main/java/io/netty/resolver/HostsFileParser.java b/resolver/src/main/java/io/netty/resolver/HostsFileParser.java index 1997fe9e1652..16eaba6167f9 100644 --- a/resolver/src/main/java/io/netty/resolver/HostsFileParser.java +++ b/resolver/src/main/java/io/netty/resolver/HostsFileParser.java @@ -23,12 +23,14 @@ import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.io.Reader; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -66,14 +68,25 @@ private static File locateHostsFile() { } /** - * Parse hosts file at standard OS location. + * Parse hosts file at standard OS location using the systems default {@link Charset} for decoding. * * @return a {@link HostsFileEntries} */ public static HostsFileEntries parseSilently() { + return parseSilently(Charset.defaultCharset()); + } + + /** + * Parse hosts file at standard OS location using the given {@link Charset}s one after each other until + * we were able to parse something or none is left. + * + * @param charsets the {@link Charset}s to try as file encodings when parsing. + * @return a {@link HostsFileEntries} + */ + public static HostsFileEntries parseSilently(Charset... charsets) { File hostsFile = locateHostsFile(); try { - return parse(hostsFile); + return parse(hostsFile, charsets); } catch (IOException e) { if (logger.isWarnEnabled()) { logger.warn("Failed to load and parse hosts file at " + hostsFile.getPath(), e); @@ -83,7 +96,7 @@ public static HostsFileEntries parseSilently() { } /** - * Parse hosts file at standard OS location. + * Parse hosts file at standard OS location using the system default {@link Charset} for decoding. * * @return a {@link HostsFileEntries} * @throws IOException file could not be read @@ -93,19 +106,37 @@ public static HostsFileEntries parse() throws IOException { } /** - * Parse a hosts file. + * Parse a hosts file using the system default {@link Charset} for decoding. * * @param file the file to be parsed * @return a {@link HostsFileEntries} * @throws IOException file could not be read */ public static HostsFileEntries parse(File file) throws IOException { + return parse(file, Charset.defaultCharset()); + } + + /** + * Parse a hosts file. + * + * @param file the file to be parsed + * @param charsets the {@link Charset}s to try as file encodings when parsing. + * @return a {@link HostsFileEntries} + * @throws IOException file could not be read + */ + public static HostsFileEntries parse(File file, Charset... charsets) throws IOException { checkNotNull(file, "file"); + checkNotNull(charsets, "charsets"); if (file.exists() && file.isFile()) { - return parse(new BufferedReader(new FileReader(file))); - } else { - return HostsFileEntries.EMPTY; + for (Charset charset: charsets) { + HostsFileEntries entries = parse(new BufferedReader(new InputStreamReader( + new FileInputStream(file), charset))); + if (entries != HostsFileEntries.EMPTY) { + return entries; + } + } } + return HostsFileEntries.EMPTY; } /** diff --git a/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java b/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java index 6b908f574205..ac4e4b2eb695 100644 --- a/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java +++ b/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java @@ -15,13 +15,18 @@ */ package io.netty.resolver; +import io.netty.util.CharsetUtil; +import org.junit.Assume; import org.junit.Test; import java.io.BufferedReader; +import java.io.File; import java.io.IOException; import java.io.StringReader; import java.net.Inet4Address; import java.net.Inet6Address; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; import java.util.Map; import static org.junit.Assert.*; @@ -60,4 +65,41 @@ public void testParse() throws IOException { assertEquals("192.168.0.5", inet4Entries.get("host7").getHostAddress()); assertEquals("0:0:0:0:0:0:0:1", inet6Entries.get("host1").getHostAddress()); } + + @Test + public void testParseUnicode() throws IOException { + final Charset unicodeCharset; + try { + unicodeCharset = Charset.forName("unicode"); + } catch (UnsupportedCharsetException e) { + Assume.assumeNoException(e); + return; + } + testParseFile(HostsFileParser.parse( + new File(getClass().getResource("hosts-unicode").getFile()), unicodeCharset)); + } + + @Test + public void testParseMultipleCharsets() throws IOException { + final Charset unicodeCharset; + try { + unicodeCharset = Charset.forName("unicode"); + } catch (UnsupportedCharsetException e) { + Assume.assumeNoException(e); + return; + } + testParseFile(HostsFileParser.parse(new File(getClass().getResource("hosts-unicode").getFile()), + CharsetUtil.UTF_8, CharsetUtil.ISO_8859_1, unicodeCharset)); + } + + private static void testParseFile(HostsFileEntries entries) throws IOException { + Map inet4Entries = entries.inet4Entries(); + Map inet6Entries = entries.inet6Entries(); + + assertEquals("Expected 2 IPv4 entries", 2, inet4Entries.size()); + assertEquals("Expected 1 IPv6 entries", 1, inet6Entries.size()); + assertEquals("127.0.0.1", inet4Entries.get("localhost").getHostAddress()); + assertEquals("255.255.255.255", inet4Entries.get("broadcasthost").getHostAddress()); + assertEquals("0:0:0:0:0:0:0:1", inet6Entries.get("localhost").getHostAddress()); + } } diff --git a/resolver/src/test/resources/io/netty/resolver/hosts-unicode b/resolver/src/test/resources/io/netty/resolver/hosts-unicode new file mode 100644 index 0000000000000000000000000000000000000000..68750bfd947a62c95e78744d61f88f5e2f2fb4b9 GIT binary patch literal 426 zcmZ`#+X})k3_Y*gR}}U>tRRBVKKL8m#cnt^=_=yidy?u9FN8F0b8>PL6ey6>4Gx$v zBl5O~V|1wKGg{3j%s@=d-vQl%?ujwDDzf1%GGmCEFye?Fp1D94$#fcpWx_@IYn+(r ziH*z!RyfwCo(4Cq7~g7V+*M_Zv(`!PO8KI)c$EB+8AQd%Qzoj(gg64=lz^JCu&10|HO={0;?Pv-I#3lwX+;MnpK^m_1k{*djN#5LLC4A literal 0 HcmV?d00001 From 37a4f99f2561522a1aa57b0037521d407fb926f3 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 25 Aug 2018 07:26:56 +0200 Subject: [PATCH 134/417] Use Java11+ea28 during build. (#8113) Motivation: We should ensure we use the latest Java11 EA during build to catch any regressions etc. Modifications: Update from ea19 to ea28. Result: Use latest Java11 release. --- docker/docker-compose.centos-6.111.yaml | 2 +- docker/docker-compose.centos-7.111.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml index 61e21b636b19..346fe72e0e7a 100644 --- a/docker/docker-compose.centos-6.111.yaml +++ b/docker/docker-compose.centos-6.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "1.11.0-19" + java_version : "1.11.0-28" test: image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-7.111.yaml b/docker/docker-compose.centos-7.111.yaml index aea7143e8622..5bd4a542e7fa 100644 --- a/docker/docker-compose.centos-7.111.yaml +++ b/docker/docker-compose.centos-7.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "1.11.0-19" + java_version : "1.11.0-28" test: image: netty:centos-7-1.11 From 1bd9e662ddfbc1ef57e24de9400215401d49e911 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 25 Aug 2018 08:14:38 +0200 Subject: [PATCH 135/417] Clarify deprecation docs a bit. (#8226) Motivation: It seems to sometimes confuse people what to do to replace setMaxMessagePerRead(...). Modifications: Add some more details to the javadocs about the correct replacement. Result: Related to https://github.com/netty/netty/issues/8214. --- transport/src/main/java/io/netty/channel/ChannelConfig.java | 6 ++++-- transport/src/main/java/io/netty/channel/ChannelOption.java | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/ChannelConfig.java b/transport/src/main/java/io/netty/channel/ChannelConfig.java index 2a7f848d3605..9550acd01e28 100644 --- a/transport/src/main/java/io/netty/channel/ChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/ChannelConfig.java @@ -121,7 +121,8 @@ public interface ChannelConfig { ChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis); /** - * @deprecated Use {@link MaxMessagesRecvByteBufAllocator} + * @deprecated Use {@link MaxMessagesRecvByteBufAllocator} and + * {@link MaxMessagesRecvByteBufAllocator#maxMessagesPerRead()}. *

* Returns the maximum number of messages to read per read loop. * a {@link ChannelInboundHandler#channelRead(ChannelHandlerContext, Object) channelRead()} event. @@ -131,7 +132,8 @@ public interface ChannelConfig { int getMaxMessagesPerRead(); /** - * @deprecated Use {@link MaxMessagesRecvByteBufAllocator} + * @deprecated Use {@link MaxMessagesRecvByteBufAllocator} and + * {@link MaxMessagesRecvByteBufAllocator#maxMessagesPerRead(int)}. *

* Sets the maximum number of messages to read per read loop. * If this value is greater than 1, an event loop might attempt to read multiple times to procure multiple messages. diff --git a/transport/src/main/java/io/netty/channel/ChannelOption.java b/transport/src/main/java/io/netty/channel/ChannelOption.java index d026a30a5db3..97bf31545c4d 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOption.java +++ b/transport/src/main/java/io/netty/channel/ChannelOption.java @@ -78,6 +78,7 @@ public static ChannelOption newInstance(String name) { public static final ChannelOption CONNECT_TIMEOUT_MILLIS = valueOf("CONNECT_TIMEOUT_MILLIS"); /** * @deprecated Use {@link MaxMessagesRecvByteBufAllocator} + * and {@link MaxMessagesRecvByteBufAllocator#maxMessagesPerRead(int)}. */ @Deprecated public static final ChannelOption MAX_MESSAGES_PER_READ = valueOf("MAX_MESSAGES_PER_READ"); From 338ef9693185f483076566bfa6291979bcd4287e Mon Sep 17 00:00:00 2001 From: zhaojigang Date: Fri, 24 Aug 2018 21:05:01 +0800 Subject: [PATCH 136/417] Recycler will produce npe error when multiple recycled at different thread Motivation: Recycler may produce a NPE when the same object is recycled multiple times from different threads. Modifications: - Check if the id has changed or if the Stack became null and if so throw an IllegalStateException - Add unit test Result: Fixes https://github.com/netty/netty/issues/8220. --- .../src/main/java/io/netty/util/Recycler.java | 6 ++++ .../test/java/io/netty/util/RecyclerTest.java | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/common/src/main/java/io/netty/util/Recycler.java b/common/src/main/java/io/netty/util/Recycler.java index 491464d9a315..87b182e6bed3 100644 --- a/common/src/main/java/io/netty/util/Recycler.java +++ b/common/src/main/java/io/netty/util/Recycler.java @@ -216,6 +216,12 @@ public void recycle(Object object) { if (object != value) { throw new IllegalArgumentException("object does not belong to handle"); } + + Stack stack = this.stack; + if (lastRecycledId != recycleId || stack == null) { + throw new IllegalStateException("recycled already"); + } + stack.push(this); } } diff --git a/common/src/test/java/io/netty/util/RecyclerTest.java b/common/src/test/java/io/netty/util/RecyclerTest.java index 6eeace5f0017..e4bcbf1da46f 100644 --- a/common/src/test/java/io/netty/util/RecyclerTest.java +++ b/common/src/test/java/io/netty/util/RecyclerTest.java @@ -81,6 +81,38 @@ public void testMultipleRecycle() { object.recycle(); } + @Test(expected = IllegalStateException.class) + public void testMultipleRecycleAtDifferentThread() throws InterruptedException { + Recycler recycler = newRecycler(1024); + final HandledObject object = recycler.get(); + final AtomicReference exceptionStore = new AtomicReference(); + final Thread thread1 = new Thread(new Runnable() { + @Override + public void run() { + object.recycle(); + } + }); + thread1.start(); + thread1.join(); + + final Thread thread2 = new Thread(new Runnable() { + @Override + public void run() { + try { + object.recycle(); + } catch (IllegalStateException e) { + exceptionStore.set(e); + } + } + }); + thread2.start(); + thread2.join(); + IllegalStateException exception = exceptionStore.get(); + if (exception != null) { + throw exception; + } + } + @Test public void testRecycle() { Recycler recycler = newRecycler(1024); From 8679c5ef43d285b22ae603965bb8254164f1570e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 28 Aug 2018 16:32:29 +0200 Subject: [PATCH 137/417] CleanerJava9 should be able to do its job even with a SecurityManager installed. (#8204) Motivation: CleanerJava9 currently fails whever a SecurityManager is installed. We should make use of AccessController.doPrivileged(...) so the user can give it the correct rights. Modifications: Use doPrivileged(...) when needed. Result: Fixes https://github.com/netty/netty/issues/8201. --- .../io/netty/util/internal/CleanerJava9.java | 69 ++++++++++++++----- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/CleanerJava9.java b/common/src/main/java/io/netty/util/internal/CleanerJava9.java index d111a7e54a58..3a0c5854c38b 100644 --- a/common/src/main/java/io/netty/util/internal/CleanerJava9.java +++ b/common/src/main/java/io/netty/util/internal/CleanerJava9.java @@ -21,6 +21,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; /** * Provide a way to clean a ByteBuffer on Java9+. @@ -34,20 +36,26 @@ final class CleanerJava9 implements Cleaner { final Method method; final Throwable error; if (PlatformDependent0.hasUnsafe()) { - ByteBuffer buffer = ByteBuffer.allocateDirect(1); - Object maybeInvokeMethod; - try { - // See https://bugs.openjdk.java.net/browse/JDK-8171377 - Method m = PlatformDependent0.UNSAFE.getClass().getDeclaredMethod("invokeCleaner", ByteBuffer.class); - m.invoke(PlatformDependent0.UNSAFE, buffer); - maybeInvokeMethod = m; - } catch (NoSuchMethodException e) { - maybeInvokeMethod = e; - } catch (InvocationTargetException e) { - maybeInvokeMethod = e; - } catch (IllegalAccessException e) { - maybeInvokeMethod = e; - } + final ByteBuffer buffer = ByteBuffer.allocateDirect(1); + Object maybeInvokeMethod = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + try { + // See https://bugs.openjdk.java.net/browse/JDK-8171377 + Method m = PlatformDependent0.UNSAFE.getClass().getDeclaredMethod( + "invokeCleaner", ByteBuffer.class); + m.invoke(PlatformDependent0.UNSAFE, buffer); + return m; + } catch (NoSuchMethodException e) { + return e; + } catch (InvocationTargetException e) { + return e; + } catch (IllegalAccessException e) { + return e; + } + } + }); + if (maybeInvokeMethod instanceof Throwable) { method = null; error = (Throwable) maybeInvokeMethod; @@ -73,10 +81,35 @@ static boolean isSupported() { @Override public void freeDirectBuffer(ByteBuffer buffer) { - try { - INVOKE_CLEANER.invoke(PlatformDependent0.UNSAFE, buffer); - } catch (Throwable cause) { - PlatformDependent0.throwException(cause); + // Try to minimize overhead when there is no SecurityManager present. + // See https://bugs.openjdk.java.net/browse/JDK-8191053. + if (System.getSecurityManager() == null) { + try { + INVOKE_CLEANER.invoke(PlatformDependent0.UNSAFE, buffer); + } catch (Throwable cause) { + PlatformDependent0.throwException(cause); + } + } else { + freeDirectBufferPrivileged(buffer); + } + } + + private static void freeDirectBufferPrivileged(final ByteBuffer buffer) { + Exception error = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Exception run() { + try { + INVOKE_CLEANER.invoke(PlatformDependent0.UNSAFE, buffer); + } catch (InvocationTargetException e) { + return e; + } catch (IllegalAccessException e) { + return e; + } + return null; + } + }); + if (error != null) { + PlatformDependent0.throwException(error); } } } From f77891cc1786806630f6c4408d5d37abb1891e7b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 29 Aug 2018 08:21:07 +0200 Subject: [PATCH 138/417] We should prefer direct buffers if we can access the cleaner even if sun.misc.Unsafe is not present. (#8233) Motivation: We should prefer direct buffers whenever we can use the cleaner even if sun.misc.Unsafe is not present. Modifications: Correctly prefer direct buffers in all cases. Result: More correct code. --- .../io/netty/util/internal/PlatformDependent.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index d7a8a262185b..6a534bcff0af 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -79,8 +79,7 @@ public final class PlatformDependent { private static final boolean CAN_ENABLE_TCP_NODELAY_BY_DEFAULT = !isAndroid(); private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause0(); - private static final boolean DIRECT_BUFFER_PREFERRED = - UNSAFE_UNAVAILABILITY_CAUSE == null && !SystemPropertyUtil.getBoolean("io.netty.noPreferDirect", false); + private static final boolean DIRECT_BUFFER_PREFERRED; private static final long MAX_DIRECT_MEMORY = maxDirectMemory0(); private static final int MPSC_CHUNK_SIZE = 1024; @@ -128,9 +127,6 @@ public Random current() { } }; } - if (logger.isDebugEnabled()) { - logger.debug("-Dio.netty.noPreferDirect: {}", !DIRECT_BUFFER_PREFERRED); - } /* * We do not want to log this message if unsafe is explicitly disabled. Do not remove the explicit no unsafe @@ -190,6 +186,13 @@ public Random current() { } else { CLEANER = NOOP; } + + // We should always prefer direct buffers by default if we can use a Cleaner to release direct buffers. + DIRECT_BUFFER_PREFERRED = CLEANER != NOOP + && !SystemPropertyUtil.getBoolean("io.netty.noPreferDirect", false); + if (logger.isDebugEnabled()) { + logger.debug("-Dio.netty.noPreferDirect: {}", !DIRECT_BUFFER_PREFERRED); + } } public static boolean hasDirectBufferNoCleanerConstructor() { From 5aaa16b24cee9d7455ea1cbb4cb9f2e43eb931b7 Mon Sep 17 00:00:00 2001 From: Roger Date: Wed, 29 Aug 2018 08:14:26 -0400 Subject: [PATCH 139/417] An unit test to ensure that cipher suites don't break/disappear between releases. (#8225) Motivation Ensure classes of cipher suites continue working between releases. Adding just a DHE check for now as it caused #8165 but this test can be expaned to other suites. Modifications Adding an unit test that checks for the presence of a cipher suite. Result Prevent #8165 from happening in the future. --- .../handler/ssl/CipherSuiteCanaryTest.java | 257 ++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java diff --git a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java new file mode 100644 index 000000000000..69837d292d42 --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java @@ -0,0 +1,257 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package io.netty.handler.ssl; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.DefaultEventLoopGroup; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.local.LocalChannel; +import io.netty.channel.local.LocalServerChannel; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import io.netty.handler.ssl.util.SelfSignedCertificate; +import io.netty.util.ReferenceCountUtil; +import io.netty.util.concurrent.Promise; + +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.junit.AfterClass; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import static org.junit.Assert.assertTrue; + +/** + * The purpose of this unit test is to act as a canary and catch changes in supported cipher suites. + */ +@RunWith(Parameterized.class) +public class CipherSuiteCanaryTest { + + private static EventLoopGroup GROUP; + + private static SelfSignedCertificate CERT; + + @Parameters(name = "{index}: serverSslProvider = {0}, clientSslProvider = {1}, rfcCipherName = {2}") + public static Collection parameters() { + List dst = new ArrayList(); + dst.addAll(expand("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")); // DHE-RSA-AES128-GCM-SHA256 + return dst; + } + + @BeforeClass + public static void init() throws Exception { + GROUP = new DefaultEventLoopGroup(); + CERT = new SelfSignedCertificate(); + } + + @AfterClass + public static void destory() { + GROUP.shutdownGracefully(); + CERT.delete(); + } + + private final SslProvider serverSslProvider; + + private final SslProvider clientSslProvider; + + private final String rfcCipherName; + + public CipherSuiteCanaryTest(SslProvider serverSslProvider, SslProvider clientSslProvider, String rfcCipherName) { + this.serverSslProvider = serverSslProvider; + this.clientSslProvider = clientSslProvider; + this.rfcCipherName = rfcCipherName; + } + + @Test + public void testHandshake() throws Exception { + Assume.assumeTrue("Unsupported cipher: " + rfcCipherName, OpenSsl.isCipherSuiteAvailable(rfcCipherName)); + + List ciphers = Arrays.asList(rfcCipherName); + + final SslContext sslServerContext = SslContextBuilder.forServer(CERT.certificate(), CERT.privateKey()) + .sslProvider(serverSslProvider) + .ciphers(ciphers) + .build(); + + try { + final SslContext sslClientContext = SslContextBuilder.forClient() + .sslProvider(clientSslProvider) + .ciphers(ciphers) + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .build(); + + try { + final ByteBuf request = Unpooled.wrappedBuffer(new byte[] {'P', 'I', 'N', 'G'}); + final ByteBuf response = Unpooled.wrappedBuffer(new byte[] {'P', 'O', 'N', 'G'}); + + final Promise serverPromise = GROUP.next().newPromise(); + final Promise clientPromise = GROUP.next().newPromise(); + + ChannelHandler serverHandler = new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast(sslServerContext.newHandler(ch.alloc())); + + pipeline.addLast(new ChannelInboundHandlerAdapter() { + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + serverPromise.cancel(true); + ctx.fireChannelInactive(); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (serverPromise.trySuccess(msg)) { + ctx.writeAndFlush(response.slice()); + } + + ctx.close(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (!serverPromise.tryFailure(cause)) { + ctx.fireExceptionCaught(cause); + } + } + }); + } + }; + + LocalAddress address = new LocalAddress("test-" + serverSslProvider + + "-" + clientSslProvider + "-" + rfcCipherName); + + Channel server = server(address, serverHandler); + try { + ChannelHandler clientHandler = new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast(sslClientContext.newHandler(ch.alloc())); + + pipeline.addLast(new ChannelInboundHandlerAdapter() { + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + clientPromise.cancel(true); + ctx.fireChannelInactive(); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + clientPromise.trySuccess(msg); + ctx.close(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) + throws Exception { + if (!clientPromise.tryFailure(cause)) { + ctx.fireExceptionCaught(cause); + } + } + }); + } + }; + + Channel client = client(server, clientHandler); + try { + client.writeAndFlush(request.slice()); + + assertTrue("client timeout", clientPromise.await(5L, TimeUnit.SECONDS)); + assertTrue("server timeout", serverPromise.await(5L, TimeUnit.SECONDS)); + + clientPromise.sync(); + serverPromise.sync(); + } finally { + client.close().sync(); + } + } finally { + server.close().sync(); + } + } finally { + ReferenceCountUtil.release(sslClientContext); + } + } finally { + ReferenceCountUtil.release(sslServerContext); + } + } + + private static Channel server(LocalAddress address, ChannelHandler handler) throws Exception { + ServerBootstrap bootstrap = new ServerBootstrap() + .channel(LocalServerChannel.class) + .group(GROUP) + .childHandler(handler); + + return bootstrap.bind(address).sync().channel(); + } + + private static Channel client(Channel server, ChannelHandler handler) throws Exception { + SocketAddress remoteAddress = server.localAddress(); + + Bootstrap bootstrap = new Bootstrap() + .channel(LocalChannel.class) + .group(GROUP) + .handler(handler); + + return bootstrap.connect(remoteAddress).sync().channel(); + } + + private static List expand(String rfcCipherName) { + List dst = new ArrayList(); + SslProvider[] sslProviders = SslProvider.values(); + + for (int i = 0; i < sslProviders.length; i++) { + SslProvider serverSslProvider = sslProviders[i]; + + for (int j = 0; j < sslProviders.length; j++) { + SslProvider clientSslProvider = sslProviders[j]; + + if ((serverSslProvider != SslProvider.JDK || clientSslProvider != SslProvider.JDK) + && !OpenSsl.isAvailable()) { + continue; + } + + dst.add(new Object[]{serverSslProvider, clientSslProvider, rfcCipherName}); + } + } + + if (dst.isEmpty()) { + throw new IllegalStateException(); + } + + return dst; + } +} From 54f565ac676e94992f67a48ae0b29ec2b5152ad4 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 29 Aug 2018 19:36:33 +0200 Subject: [PATCH 140/417] =?UTF-8?q?Allow=20to=20use=20native=20transports?= =?UTF-8?q?=20when=20sun.misc.Unsafe=20is=20not=20present=20on=E2=80=A6=20?= =?UTF-8?q?(#8231)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow to use native transports when sun.misc.Unsafe is not present on the system Motivation: We should be able to use the native transports (epoll / kqueue) even when sun.misc.Unsafe is not present on the system. This is especially important as Java11 will be released soon and does not allow access to it by default. Modifications: - Correctly disable usage of sun.misc.Unsafe when -PnoUnsafe is used while running the build - Correctly increment metric when UnpooledDirectByteBuf is allocated. This was uncovered once -PnoUnsafe usage was fixed. - Implement fallbacks in all our native transport code for when sun.misc.Unsafe is not present. Result: Fixes https://github.com/netty/netty/issues/8229. --- .../netty/buffer/UnpooledDirectByteBuf.java | 2 +- .../buffer/FixedCompositeByteBufTest.java | 4 +- pom.xml | 2 +- .../src/main/c/netty_epoll_native.c | 5 + .../epoll/AbstractEpollStreamChannel.java | 23 +--- .../java/io/netty/channel/epoll/Epoll.java | 11 +- .../netty/channel/epoll/EpollEventArray.java | 36 +++-- .../epoll/EpollRecvByteAllocatorHandle.java | 9 +- .../src/main/c/netty_kqueue_native.c | 5 + .../kqueue/AbstractKQueueStreamChannel.java | 24 +--- .../java/io/netty/channel/kqueue/KQueue.java | 11 +- .../channel/kqueue/KQueueEventArray.java | 80 ++++++++--- .../kqueue/KQueueRecvByteAllocatorHandle.java | 11 +- .../netty/channel/kqueue/NativeLongArray.java | 54 +++++--- .../src/main/c/netty_unix_buffer.c | 52 +++++++ .../src/main/c/netty_unix_buffer.h | 25 ++++ .../java/io/netty/channel/unix/Buffer.java | 68 +++++++++ .../java/io/netty/channel/unix/IovArray.java | 60 +++++--- .../unix/PreferredDirectByteBufAllocator.java | 130 ++++++++++++++++++ 19 files changed, 483 insertions(+), 129 deletions(-) create mode 100644 transport-native-unix-common/src/main/c/netty_unix_buffer.c create mode 100644 transport-native-unix-common/src/main/c/netty_unix_buffer.h create mode 100644 transport-native-unix-common/src/main/java/io/netty/channel/unix/Buffer.java create mode 100644 transport-native-unix-common/src/main/java/io/netty/channel/unix/PreferredDirectByteBufAllocator.java diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java index 49960b376411..74c0509a7603 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java @@ -64,7 +64,7 @@ public UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int ma } this.alloc = alloc; - setByteBuffer(ByteBuffer.allocateDirect(initialCapacity)); + setByteBuffer(allocateDirect(initialCapacity)); } /** diff --git a/buffer/src/test/java/io/netty/buffer/FixedCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/FixedCompositeByteBufTest.java index 481c70fc8430..b6260f7848bf 100644 --- a/buffer/src/test/java/io/netty/buffer/FixedCompositeByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/FixedCompositeByteBufTest.java @@ -401,7 +401,7 @@ public void testHasMemoryAddressWhenEmpty() { buf.release(); } - @Test(expected = UnsupportedOperationException.class) + @Test public void testHasNoMemoryAddressWhenMultipleBuffers() { ByteBuf buf1 = directBuffer(10); if (!buf1.hasMemoryAddress()) { @@ -415,6 +415,8 @@ public void testHasNoMemoryAddressWhenMultipleBuffers() { try { buf.memoryAddress(); fail(); + } catch (UnsupportedOperationException expected) { + // expected } finally { buf.release(); } diff --git a/pom.xml b/pom.xml index f0389cdddd74..d9a15597cb1e 100644 --- a/pom.xml +++ b/pom.xml @@ -136,7 +136,7 @@ noUnsafe - -Dio.netty.noUnsafe + -Dio.netty.noUnsafe=true diff --git a/transport-native-epoll/src/main/c/netty_epoll_native.c b/transport-native-epoll/src/main/c/netty_epoll_native.c index 68ef0ed2c0aa..64ef61feab10 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_native.c +++ b/transport-native-epoll/src/main/c/netty_epoll_native.c @@ -38,6 +38,7 @@ #include #include "netty_epoll_linuxsocket.h" +#include "netty_unix_buffer.h" #include "netty_unix_errors.h" #include "netty_unix_filedescriptor.h" #include "netty_unix_jni.h" @@ -452,6 +453,9 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { return JNI_ERR; } + if (netty_unix_buffer_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { + return JNI_ERR; + } if (netty_epoll_linuxsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { return JNI_ERR; } @@ -501,6 +505,7 @@ static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) { netty_unix_errors_JNI_OnUnLoad(env); netty_unix_filedescriptor_JNI_OnUnLoad(env); netty_unix_socket_JNI_OnUnLoad(env); + netty_unix_buffer_JNI_OnUnLoad(env); netty_epoll_linuxsocket_JNI_OnUnLoad(env); } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java index f1fda0820664..70208d2672e3 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java @@ -513,22 +513,13 @@ protected int doWriteSingle(ChannelOutboundBuffer in) throws Exception { */ private int doWriteMultiple(ChannelOutboundBuffer in) throws Exception { final long maxBytesPerGatheringWrite = config().getMaxBytesPerGatheringWrite(); - if (PlatformDependent.hasUnsafe()) { - IovArray array = ((EpollEventLoop) eventLoop()).cleanIovArray(); - array.maxBytes(maxBytesPerGatheringWrite); - in.forEachFlushedMessage(array); - - if (array.count() >= 1) { - // TODO: Handle the case where cnt == 1 specially. - return writeBytesMultiple(in, array); - } - } else { - ByteBuffer[] buffers = in.nioBuffers(); - int cnt = in.nioBufferCount(); - if (cnt >= 1) { - // TODO: Handle the case where cnt == 1 specially. - return writeBytesMultiple(in, buffers, cnt, in.nioBufferSize(), maxBytesPerGatheringWrite); - } + IovArray array = ((EpollEventLoop) eventLoop()).cleanIovArray(); + array.maxBytes(maxBytesPerGatheringWrite); + in.forEachFlushedMessage(array); + + if (array.count() >= 1) { + // TODO: Handle the case where cnt == 1 specially. + return writeBytesMultiple(in, array); } // cnt == 0, which means the outbound buffer contained empty buffers only. in.removeBytes(0); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Epoll.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Epoll.java index b1e166ba737a..e4ecf42707b5 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Epoll.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Epoll.java @@ -16,7 +16,6 @@ package io.netty.channel.epoll; import io.netty.channel.unix.FileDescriptor; -import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.SystemPropertyUtil; /** @@ -58,15 +57,7 @@ public final class Epoll { } } - if (cause != null) { - UNAVAILABILITY_CAUSE = cause; - } else { - UNAVAILABILITY_CAUSE = PlatformDependent.hasUnsafe() - ? null - : new IllegalStateException( - "sun.misc.Unsafe not available", - PlatformDependent.getUnsafeUnavailabilityCause()); - } + UNAVAILABILITY_CAUSE = cause; } /** diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventArray.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventArray.java index 2d84703bafcb..eff9b66341aa 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventArray.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventArray.java @@ -15,8 +15,11 @@ */ package io.netty.channel.epoll; +import io.netty.channel.unix.Buffer; import io.netty.util.internal.PlatformDependent; +import java.nio.ByteBuffer; + /** * This is an internal datastructure which can be directly passed to epoll_wait to reduce the overhead. * @@ -41,6 +44,7 @@ final class EpollEventArray { // The offsiet of the data union in the epoll_event struct private static final int EPOLL_DATA_OFFSET = Native.offsetofEpollData(); + private ByteBuffer memory; private long memoryAddress; private int length; @@ -49,11 +53,8 @@ final class EpollEventArray { throw new IllegalArgumentException("length must be >= 1 but was " + length); } this.length = length; - memoryAddress = allocate(length); - } - - private static long allocate(int length) { - return PlatformDependent.allocateMemory(length * EPOLL_EVENT_SIZE); + memory = Buffer.allocateDirectWithNativeOrder(calculateBufferCapacity(length)); + memoryAddress = Buffer.memoryAddress(memory); } /** @@ -77,28 +78,43 @@ int length() { void increase() { // double the size length <<= 1; - free(); - memoryAddress = allocate(length); + // There is no need to preserve what was in the memory before. + ByteBuffer buffer = Buffer.allocateDirectWithNativeOrder(calculateBufferCapacity(length)); + Buffer.free(memory); + memory = buffer; + memoryAddress = Buffer.memoryAddress(buffer); } /** * Free this {@link EpollEventArray}. Any usage after calling this method may segfault the JVM! */ void free() { - PlatformDependent.freeMemory(memoryAddress); + Buffer.free(memory); + memoryAddress = 0; } /** * Return the events for the {@code epoll_event} on this index. */ int events(int index) { - return PlatformDependent.getInt(memoryAddress + index * EPOLL_EVENT_SIZE); + return getInt(index, 0); } /** * Return the file descriptor for the {@code epoll_event} on this index. */ int fd(int index) { - return PlatformDependent.getInt(memoryAddress + index * EPOLL_EVENT_SIZE + EPOLL_DATA_OFFSET); + return getInt(index, EPOLL_DATA_OFFSET); + } + + private int getInt(int index, int offset) { + if (PlatformDependent.hasUnsafe()) { + return PlatformDependent.getInt(memoryAddress + index * EPOLL_EVENT_SIZE + offset); + } + return memory.getInt(index * EPOLL_EVENT_SIZE + offset); + } + + private static int calculateBufferCapacity(int capacity) { + return capacity * EPOLL_EVENT_SIZE; } } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorHandle.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorHandle.java index 688cf4f7e9e3..a500a891b031 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorHandle.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorHandle.java @@ -19,10 +19,13 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelConfig; import io.netty.channel.RecvByteBufAllocator; +import io.netty.channel.unix.PreferredDirectByteBufAllocator; import io.netty.util.UncheckedBooleanSupplier; import io.netty.util.internal.ObjectUtil; class EpollRecvByteAllocatorHandle implements RecvByteBufAllocator.ExtendedHandle { + private final PreferredDirectByteBufAllocator preferredDirectByteBufAllocator = + new PreferredDirectByteBufAllocator(); private final RecvByteBufAllocator.ExtendedHandle delegate; private final UncheckedBooleanSupplier defaultMaybeMoreDataSupplier = new UncheckedBooleanSupplier() { @Override @@ -34,7 +37,7 @@ public boolean get() { private boolean receivedRdHup; EpollRecvByteAllocatorHandle(RecvByteBufAllocator.ExtendedHandle handle) { - this.delegate = ObjectUtil.checkNotNull(handle, "handle"); + delegate = ObjectUtil.checkNotNull(handle, "handle"); } final void receivedRdHup() { @@ -69,7 +72,9 @@ final boolean isEdgeTriggered() { @Override public final ByteBuf allocate(ByteBufAllocator alloc) { - return delegate.allocate(alloc); + // We need to ensure we always allocate a direct ByteBuf as we can only use a direct buffer to read via JNI. + preferredDirectByteBufAllocator.updateAllocator(alloc); + return delegate.allocate(preferredDirectByteBufAllocator); } @Override diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_native.c b/transport-native-kqueue/src/main/c/netty_kqueue_native.c index 389569735e15..82222c80cc71 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_native.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_native.c @@ -28,6 +28,7 @@ #include "netty_kqueue_bsdsocket.h" #include "netty_kqueue_eventarray.h" +#include "netty_unix_buffer.h" #include "netty_unix_errors.h" #include "netty_unix_filedescriptor.h" #include "netty_unix_jni.h" @@ -293,6 +294,9 @@ static jint netty_kqueue_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefi if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { return JNI_ERR; } + if (netty_unix_buffer_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { + return JNI_ERR; + } if (netty_kqueue_bsdsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { return JNI_ERR; } @@ -314,6 +318,7 @@ static void netty_kqueue_native_JNI_OnUnLoad(JNIEnv* env) { netty_unix_errors_JNI_OnUnLoad(env); netty_unix_filedescriptor_JNI_OnUnLoad(env); netty_unix_socket_JNI_OnUnLoad(env); + netty_unix_buffer_JNI_OnUnLoad(env); netty_kqueue_bsdsocket_JNI_OnUnLoad(env); netty_kqueue_eventarray_JNI_OnUnLoad(env); } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueStreamChannel.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueStreamChannel.java index 46f4fc72ee2c..7d604f150acb 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueStreamChannel.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueStreamChannel.java @@ -33,7 +33,6 @@ import io.netty.channel.unix.IovArray; import io.netty.channel.unix.SocketWritableByteChannel; import io.netty.channel.unix.UnixChannelUtil; -import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import io.netty.util.internal.UnstableApi; import io.netty.util.internal.logging.InternalLogger; @@ -345,22 +344,13 @@ protected int doWriteSingle(ChannelOutboundBuffer in) throws Exception { */ private int doWriteMultiple(ChannelOutboundBuffer in) throws Exception { final long maxBytesPerGatheringWrite = config().getMaxBytesPerGatheringWrite(); - if (PlatformDependent.hasUnsafe()) { - IovArray array = ((KQueueEventLoop) eventLoop()).cleanArray(); - array.maxBytes(maxBytesPerGatheringWrite); - in.forEachFlushedMessage(array); - - if (array.count() >= 1) { - // TODO: Handle the case where cnt == 1 specially. - return writeBytesMultiple(in, array); - } - } else { - ByteBuffer[] buffers = in.nioBuffers(); - int cnt = in.nioBufferCount(); - if (cnt >= 1) { - // TODO: Handle the case where cnt == 1 specially. - return writeBytesMultiple(in, buffers, cnt, in.nioBufferSize(), maxBytesPerGatheringWrite); - } + IovArray array = ((KQueueEventLoop) eventLoop()).cleanArray(); + array.maxBytes(maxBytesPerGatheringWrite); + in.forEachFlushedMessage(array); + + if (array.count() >= 1) { + // TODO: Handle the case where cnt == 1 specially. + return writeBytesMultiple(in, array); } // cnt == 0, which means the outbound buffer contained empty buffers only. in.removeBytes(0); diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java index b5ec5a51bb92..b5a772fab799 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java @@ -16,7 +16,6 @@ package io.netty.channel.kqueue; import io.netty.channel.unix.FileDescriptor; -import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.UnstableApi; @@ -48,15 +47,7 @@ public final class KQueue { } } - if (cause != null) { - UNAVAILABILITY_CAUSE = cause; - } else { - UNAVAILABILITY_CAUSE = PlatformDependent.hasUnsafe() - ? null - : new IllegalStateException( - "sun.misc.Unsafe not available", - PlatformDependent.getUnsafeUnavailabilityCause()); - } + UNAVAILABILITY_CAUSE = cause; } /** diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventArray.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventArray.java index 636d55a81445..4a263280a680 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventArray.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventArray.java @@ -15,8 +15,11 @@ */ package io.netty.channel.kqueue; +import io.netty.channel.unix.Buffer; import io.netty.util.internal.PlatformDependent; +import java.nio.ByteBuffer; + /** * Represents an array of kevent structures, backed by offheap memory. * @@ -37,6 +40,7 @@ final class KQueueEventArray { private static final int KQUEUE_FLAGS_OFFSET = Native.offsetofKEventFlags(); private static final int KQUEUE_DATA_OFFSET = Native.offsetofKeventData(); + private ByteBuffer memory; private long memoryAddress; private int size; private int capacity; @@ -45,7 +49,8 @@ final class KQueueEventArray { if (capacity < 1) { throw new IllegalArgumentException("capacity must be >= 1 but was " + capacity); } - memoryAddress = PlatformDependent.allocateMemory(capacity * KQUEUE_EVENT_SIZE); + memory = Buffer.allocateDirectWithNativeOrder(calculateBufferCapacity(capacity)); + memoryAddress = Buffer.memoryAddress(memory); this.capacity = capacity; } @@ -73,11 +78,11 @@ void clear() { } void evSet(AbstractKQueueChannel ch, short filter, short flags, int fflags) { - checkSize(); - evSet(getKEventOffset(size++), ch, ch.socket.intValue(), filter, flags, fflags); + reallocIfNeeded(); + evSet(getKEventOffset(size++) + memoryAddress, ch, ch.socket.intValue(), filter, flags, fflags); } - private void checkSize() { + private void reallocIfNeeded() { if (size == capacity) { realloc(true); } @@ -89,15 +94,25 @@ private void checkSize() { void realloc(boolean throwIfFail) { // Double the capacity while it is "sufficiently small", and otherwise increase by 50%. int newLength = capacity <= 65536 ? capacity << 1 : capacity + capacity >> 1; - long newMemoryAddress = PlatformDependent.reallocateMemory(memoryAddress, newLength * KQUEUE_EVENT_SIZE); - if (newMemoryAddress != 0) { - memoryAddress = newMemoryAddress; - capacity = newLength; - return; - } - if (throwIfFail) { - throw new OutOfMemoryError("unable to allocate " + newLength + " new bytes! Existing capacity is: " - + capacity); + + try { + ByteBuffer buffer = Buffer.allocateDirectWithNativeOrder(calculateBufferCapacity(newLength)); + // Copy over the old content of the memory and reset the position as we always act on the buffer as if + // the position was never increased. + memory.position(0).limit(size); + buffer.put(memory); + buffer.position(0); + + Buffer.free(memory); + memory = buffer; + memoryAddress = Buffer.memoryAddress(buffer); + } catch (OutOfMemoryError e) { + if (throwIfFail) { + OutOfMemoryError error = new OutOfMemoryError( + "unable to allocate " + newLength + " new bytes! Existing capacity is: " + capacity); + error.initCause(e); + throw error; + } } } @@ -105,36 +120,57 @@ void realloc(boolean throwIfFail) { * Free this {@link KQueueEventArray}. Any usage after calling this method may segfault the JVM! */ void free() { - PlatformDependent.freeMemory(memoryAddress); + Buffer.free(memory); memoryAddress = size = capacity = 0; } - long getKEventOffset(int index) { - return memoryAddress + index * KQUEUE_EVENT_SIZE; + private static int getKEventOffset(int index) { + return index * KQUEUE_EVENT_SIZE; + } + + private long getKEventOffsetAddress(int index) { + return getKEventOffset(index) + memoryAddress; + } + + private short getShort(int index, int offset) { + if (PlatformDependent.hasUnsafe()) { + return PlatformDependent.getShort(getKEventOffsetAddress(index) + offset); + } + return memory.getShort(getKEventOffset(index) + offset); } short flags(int index) { - return PlatformDependent.getShort(getKEventOffset(index) + KQUEUE_FLAGS_OFFSET); + return getShort(index, KQUEUE_FLAGS_OFFSET); } short filter(int index) { - return PlatformDependent.getShort(getKEventOffset(index) + KQUEUE_FILTER_OFFSET); + return getShort(index, KQUEUE_FILTER_OFFSET); } short fflags(int index) { - return PlatformDependent.getShort(getKEventOffset(index) + KQUEUE_FFLAGS_OFFSET); + return getShort(index, KQUEUE_FFLAGS_OFFSET); } int fd(int index) { - return PlatformDependent.getInt(getKEventOffset(index) + KQUEUE_IDENT_OFFSET); + if (PlatformDependent.hasUnsafe()) { + return PlatformDependent.getInt(getKEventOffsetAddress(index) + KQUEUE_IDENT_OFFSET); + } + return memory.getInt(getKEventOffset(index) + KQUEUE_IDENT_OFFSET); } long data(int index) { - return PlatformDependent.getLong(getKEventOffset(index) + KQUEUE_DATA_OFFSET); + if (PlatformDependent.hasUnsafe()) { + return PlatformDependent.getLong(getKEventOffsetAddress(index) + KQUEUE_DATA_OFFSET); + } + return memory.getLong(getKEventOffset(index) + KQUEUE_DATA_OFFSET); } AbstractKQueueChannel channel(int index) { - return getChannel(getKEventOffset(index)); + return getChannel(getKEventOffsetAddress(index)); + } + + private static int calculateBufferCapacity(int capacity) { + return capacity * KQUEUE_EVENT_SIZE; } private static native void evSet(long keventAddress, AbstractKQueueChannel ch, diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueRecvByteAllocatorHandle.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueRecvByteAllocatorHandle.java index 087881f46142..9eed7d49f5e8 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueRecvByteAllocatorHandle.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueRecvByteAllocatorHandle.java @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelConfig; import io.netty.channel.RecvByteBufAllocator; +import io.netty.channel.unix.PreferredDirectByteBufAllocator; import io.netty.util.UncheckedBooleanSupplier; import io.netty.util.internal.ObjectUtil; @@ -26,7 +27,10 @@ import static java.lang.Math.min; final class KQueueRecvByteAllocatorHandle implements RecvByteBufAllocator.ExtendedHandle { + private final PreferredDirectByteBufAllocator preferredDirectByteBufAllocator = + new PreferredDirectByteBufAllocator(); private final RecvByteBufAllocator.ExtendedHandle delegate; + private final UncheckedBooleanSupplier defaultMaybeMoreDataSupplier = new UncheckedBooleanSupplier() { @Override public boolean get() { @@ -38,7 +42,7 @@ public boolean get() { private long numberBytesPending; KQueueRecvByteAllocatorHandle(RecvByteBufAllocator.ExtendedHandle handle) { - this.delegate = ObjectUtil.checkNotNull(handle, "handle"); + delegate = ObjectUtil.checkNotNull(handle, "handle"); } @Override @@ -59,7 +63,10 @@ public void incMessagesRead(int numMessages) { @Override public ByteBuf allocate(ByteBufAllocator alloc) { - return overrideGuess ? alloc.ioBuffer(guess0()) : delegate.allocate(alloc); + // We need to ensure we always allocate a direct ByteBuf as we can only use a direct buffer to read via JNI. + preferredDirectByteBufAllocator.updateAllocator(alloc); + return overrideGuess ? preferredDirectByteBufAllocator.ioBuffer(guess0()) : + delegate.allocate(preferredDirectByteBufAllocator); } @Override diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/NativeLongArray.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/NativeLongArray.java index d9f00094f16a..7f83738e7084 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/NativeLongArray.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/NativeLongArray.java @@ -15,11 +15,15 @@ */ package io.netty.channel.kqueue; +import io.netty.channel.unix.Buffer; import io.netty.util.internal.PlatformDependent; +import java.nio.ByteBuffer; + import static io.netty.channel.unix.Limits.SIZEOF_JLONG; final class NativeLongArray { + private ByteBuffer memory; private long memoryAddress; private int capacity; private int size; @@ -28,13 +32,27 @@ final class NativeLongArray { if (capacity < 1) { throw new IllegalArgumentException("capacity must be >= 1 but was " + capacity); } - memoryAddress = PlatformDependent.allocateMemory(capacity * SIZEOF_JLONG); + memory = Buffer.allocateDirectWithNativeOrder(calculateBufferCapacity(capacity)); + memoryAddress = Buffer.memoryAddress(memory); this.capacity = capacity; } + private static int idx(int index) { + return index * SIZEOF_JLONG; + } + + private static int calculateBufferCapacity(int capacity) { + return capacity * SIZEOF_JLONG; + } + void add(long value) { - checkSize(); - PlatformDependent.putLong(memoryOffset(size++), value); + reallocIfNeeded(); + if (PlatformDependent.hasUnsafe()) { + PlatformDependent.putLong(memoryOffset(size), value); + } else { + memory.putLong(idx(size), value); + } + ++size; } void clear() { @@ -46,7 +64,7 @@ boolean isEmpty() { } void free() { - PlatformDependent.freeMemory(memoryAddress); + Buffer.free(memory); memoryAddress = 0; } @@ -59,25 +77,25 @@ long memoryAddressEnd() { } private long memoryOffset(int index) { - return memoryAddress + index * SIZEOF_JLONG; + return memoryAddress + idx(index); } - private void checkSize() { + private void reallocIfNeeded() { if (size == capacity) { - realloc(); - } - } + // Double the capacity while it is "sufficiently small", and otherwise increase by 50%. + int newLength = capacity <= 65536 ? capacity << 1 : capacity + capacity >> 1; + ByteBuffer buffer = Buffer.allocateDirectWithNativeOrder(calculateBufferCapacity(newLength)); + // Copy over the old content of the memory and reset the position as we always act on the buffer as if + // the position was never increased. + memory.position(0).limit(size); + buffer.put(memory); + buffer.position(0); - private void realloc() { - // Double the capacity while it is "sufficiently small", and otherwise increase by 50%. - int newLength = capacity <= 65536 ? capacity << 1 : capacity + capacity >> 1; - long newMemoryAddress = PlatformDependent.reallocateMemory(memoryAddress, newLength * SIZEOF_JLONG); - if (newMemoryAddress == 0) { - throw new OutOfMemoryError("unable to allocate " + newLength + " new bytes! Existing capacity is: " - + capacity); + Buffer.free(memory); + memory = buffer; + memoryAddress = Buffer.memoryAddress(buffer); + capacity = newLength; } - memoryAddress = newMemoryAddress; - capacity = newLength; } @Override diff --git a/transport-native-unix-common/src/main/c/netty_unix_buffer.c b/transport-native-unix-common/src/main/c/netty_unix_buffer.c new file mode 100644 index 000000000000..85bcb1e5afeb --- /dev/null +++ b/transport-native-unix-common/src/main/c/netty_unix_buffer.c @@ -0,0 +1,52 @@ +/* + * Copyright 2018 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: + * + * http://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. + */ +#include "netty_unix_jni.h" +#include "netty_unix_util.h" +#include "netty_unix_buffer.h" + +// JNI Registered Methods Begin +static jlong netty_unix_buffer_memoryAddress0(JNIEnv* env, jclass clazz, jobject buffer) { + return (jlong) (*env)->GetDirectBufferAddress(env, buffer); +} + +static jint netty_unix_buffer_addressSize0(JNIEnv* env, jclass clazz) { + return (jint) sizeof(int*); +} + +// JNI Registered Methods End + +// JNI Method Registration Table Begin +static const JNINativeMethod statically_referenced_fixed_method_table[] = { + { "memoryAddress0", "(Ljava/nio/ByteBuffer;)J", (void *) netty_unix_buffer_memoryAddress0 }, + { "addressSize0", "()I", (void *) netty_unix_buffer_addressSize0 } +}; +static const jint statically_referenced_fixed_method_table_size = sizeof(statically_referenced_fixed_method_table) / sizeof(statically_referenced_fixed_method_table[0]); +// JNI Method Registration Table End + +jint netty_unix_buffer_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { + // We must register the statically referenced methods first! + if (netty_unix_util_register_natives(env, + packagePrefix, + "io/netty/channel/unix/Buffer", + statically_referenced_fixed_method_table, + statically_referenced_fixed_method_table_size) != 0) { + return JNI_ERR; + } + + return NETTY_JNI_VERSION; +} + +void netty_unix_buffer_JNI_OnUnLoad(JNIEnv* env) { } diff --git a/transport-native-unix-common/src/main/c/netty_unix_buffer.h b/transport-native-unix-common/src/main/c/netty_unix_buffer.h new file mode 100644 index 000000000000..5dd4f4538dce --- /dev/null +++ b/transport-native-unix-common/src/main/c/netty_unix_buffer.h @@ -0,0 +1,25 @@ +/* + * Copyright 2018 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: + * + * http://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. + */ +#ifndef NETTY_UNIX_BUFFER_H_ +#define NETTY_UNIX_BUFFER_H_ + +#include + +// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. +jint netty_unix_buffer_JNI_OnLoad(JNIEnv* env, const char* packagePrefix); +void netty_unix_buffer_JNI_OnUnLoad(JNIEnv* env); + +#endif /* NETTY_UNIX_BUFFER_H_ */ diff --git a/transport-native-unix-common/src/main/java/io/netty/channel/unix/Buffer.java b/transport-native-unix-common/src/main/java/io/netty/channel/unix/Buffer.java new file mode 100644 index 000000000000..ae23bf891d0f --- /dev/null +++ b/transport-native-unix-common/src/main/java/io/netty/channel/unix/Buffer.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.unix; + +import io.netty.util.internal.PlatformDependent; +import io.netty.util.internal.UnstableApi; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +@UnstableApi +public final class Buffer { + + private Buffer() { } + + /** + * Free the direct {@link ByteBuffer}. + */ + public static void free(ByteBuffer buffer) { + PlatformDependent.freeDirectBuffer(buffer); + } + + /** + * Returns a new {@link ByteBuffer} which has the same {@link ByteOrder} as the native order of the machine. + */ + public static ByteBuffer allocateDirectWithNativeOrder(int capacity) { + return ByteBuffer.allocateDirect(capacity).order( + PlatformDependent.BIG_ENDIAN_NATIVE_ORDER ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); + } + + /** + * Returns the memory address of the given direct {@link ByteBuffer}. + */ + public static long memoryAddress(ByteBuffer buffer) { + assert buffer.isDirect(); + if (PlatformDependent.hasUnsafe()) { + return PlatformDependent.directBufferAddress(buffer); + } + return memoryAddress0(buffer); + } + + /** + * Returns the size of a pointer. + */ + public static int addressSize() { + if (PlatformDependent.hasUnsafe()) { + return PlatformDependent.addressSize(); + } + return addressSize0(); + } + + // If Unsafe can not be used we will need to do JNI calls. + private static native int addressSize0(); + private static native long memoryAddress0(ByteBuffer buffer); +} diff --git a/transport-native-unix-common/src/main/java/io/netty/channel/unix/IovArray.java b/transport-native-unix-common/src/main/java/io/netty/channel/unix/IovArray.java index 151abe609ae1..e8b4067a8606 100644 --- a/transport-native-unix-common/src/main/java/io/netty/channel/unix/IovArray.java +++ b/transport-native-unix-common/src/main/java/io/netty/channel/unix/IovArray.java @@ -25,11 +25,6 @@ import static io.netty.channel.unix.Limits.IOV_MAX; import static io.netty.channel.unix.Limits.SSIZE_MAX; import static io.netty.util.internal.ObjectUtil.checkPositive; -import static io.netty.util.internal.PlatformDependent.allocateMemory; -import static io.netty.util.internal.PlatformDependent.directBufferAddress; -import static io.netty.util.internal.PlatformDependent.freeMemory; -import static io.netty.util.internal.PlatformDependent.putInt; -import static io.netty.util.internal.PlatformDependent.putLong; import static java.lang.Math.min; /** @@ -52,7 +47,7 @@ public final class IovArray implements MessageProcessor { /** The size of an address which should be 8 for 64 bits and 4 for 32 bits. */ - private static final int ADDRESS_SIZE = PlatformDependent.addressSize(); + private static final int ADDRESS_SIZE = Buffer.addressSize(); /** * The size of an {@code iovec} struct in bytes. This is calculated as we have 2 entries each of the size of the @@ -66,13 +61,15 @@ public final class IovArray implements MessageProcessor { */ private static final int CAPACITY = IOV_MAX * IOV_SIZE; + private final ByteBuffer memory; private final long memoryAddress; private int count; private long size; private long maxBytes = SSIZE_MAX; public IovArray() { - memoryAddress = allocateMemory(CAPACITY); + memory = Buffer.allocateDirectWithNativeOrder(CAPACITY); + memoryAddress = Buffer.memoryAddress(memory); } public void clear() { @@ -91,14 +88,23 @@ public boolean add(ByteBuf buf) { if (count == IOV_MAX) { // No more room! return false; - } else if (buf.hasMemoryAddress() && buf.nioBufferCount() == 1) { + } else if (buf.nioBufferCount() == 1) { final int len = buf.readableBytes(); - return len == 0 || add(buf.memoryAddress(), buf.readerIndex(), len); + if (len == 0) { + return true; + } + if (buf.hasMemoryAddress()) { + return add(buf.memoryAddress(), buf.readerIndex(), len); + } else { + ByteBuffer nioBuffer = buf.internalNioBuffer(buf.readerIndex(), len); + return add(Buffer.memoryAddress(nioBuffer), nioBuffer.position(), len); + } } else { ByteBuffer[] buffers = buf.nioBuffers(); for (ByteBuffer nioBuffer : buffers) { final int len = nioBuffer.remaining(); - if (len != 0 && (!add(directBufferAddress(nioBuffer), nioBuffer.position(), len) || count == IOV_MAX)) { + if (len != 0 && + (!add(Buffer.memoryAddress(nioBuffer), nioBuffer.position(), len) || count == IOV_MAX)) { return false; } } @@ -107,8 +113,7 @@ public boolean add(ByteBuf buf) { } private boolean add(long addr, int offset, int len) { - final long baseOffset = memoryAddress(count); - final long lengthOffset = baseOffset + ADDRESS_SIZE; + assert addr != 0; // If there is at least 1 entry then we enforce the maximum bytes. We want to accept at least one entry so we // will attempt to write some data and make progress. @@ -121,17 +126,30 @@ private boolean add(long addr, int offset, int len) { // - http://linux.die.net/man/2/writev return false; } + final int baseOffset = idx(count); + final int lengthOffset = baseOffset + ADDRESS_SIZE; + size += len; ++count; if (ADDRESS_SIZE == 8) { // 64bit - putLong(baseOffset, addr + offset); - putLong(lengthOffset, len); + if (PlatformDependent.hasUnsafe()) { + PlatformDependent.putLong(baseOffset + memoryAddress, addr + offset); + PlatformDependent.putLong(lengthOffset + memoryAddress, len); + } else { + memory.putLong(baseOffset, addr + offset); + memory.putLong(lengthOffset, len); + } } else { assert ADDRESS_SIZE == 4; - putInt(baseOffset, (int) addr + offset); - putInt(lengthOffset, len); + if (PlatformDependent.hasUnsafe()) { + PlatformDependent.putInt(baseOffset + memoryAddress, (int) addr + offset); + PlatformDependent.putInt(lengthOffset + memoryAddress, len); + } else { + memory.putInt(baseOffset, (int) addr + offset); + memory.putInt(lengthOffset, len); + } } return true; } @@ -176,18 +194,22 @@ public long maxBytes() { * Returns the {@code memoryAddress} for the given {@code offset}. */ public long memoryAddress(int offset) { - return memoryAddress + IOV_SIZE * offset; + return memoryAddress + idx(offset); } /** * Release the {@link IovArray}. Once release further using of it may crash the JVM! */ public void release() { - freeMemory(memoryAddress); + Buffer.free(memory); } @Override public boolean processMessage(Object msg) throws Exception { - return (msg instanceof ByteBuf) && add((ByteBuf) msg); + return msg instanceof ByteBuf && add((ByteBuf) msg); + } + + private static int idx(int index) { + return IOV_SIZE * index; } } diff --git a/transport-native-unix-common/src/main/java/io/netty/channel/unix/PreferredDirectByteBufAllocator.java b/transport-native-unix-common/src/main/java/io/netty/channel/unix/PreferredDirectByteBufAllocator.java new file mode 100644 index 000000000000..976e82cc0e78 --- /dev/null +++ b/transport-native-unix-common/src/main/java/io/netty/channel/unix/PreferredDirectByteBufAllocator.java @@ -0,0 +1,130 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.unix; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.CompositeByteBuf; +import io.netty.util.internal.UnstableApi; + +@UnstableApi +public final class PreferredDirectByteBufAllocator implements ByteBufAllocator { + private ByteBufAllocator allocator; + + public void updateAllocator(ByteBufAllocator allocator) { + this.allocator = allocator; + } + + @Override + public ByteBuf buffer() { + return allocator.directBuffer(); + } + + @Override + public ByteBuf buffer(int initialCapacity) { + return allocator.directBuffer(initialCapacity); + } + + @Override + public ByteBuf buffer(int initialCapacity, int maxCapacity) { + return allocator.directBuffer(initialCapacity, maxCapacity); + } + + @Override + public ByteBuf ioBuffer() { + return allocator.directBuffer(); + } + + @Override + public ByteBuf ioBuffer(int initialCapacity) { + return allocator.directBuffer(initialCapacity); + } + + @Override + public ByteBuf ioBuffer(int initialCapacity, int maxCapacity) { + return allocator.directBuffer(initialCapacity, maxCapacity); + } + + @Override + public ByteBuf heapBuffer() { + return allocator.heapBuffer(); + } + + @Override + public ByteBuf heapBuffer(int initialCapacity) { + return allocator.heapBuffer(initialCapacity); + } + + @Override + public ByteBuf heapBuffer(int initialCapacity, int maxCapacity) { + return allocator.heapBuffer(initialCapacity, maxCapacity); + } + + @Override + public ByteBuf directBuffer() { + return allocator.directBuffer(); + } + + @Override + public ByteBuf directBuffer(int initialCapacity) { + return allocator.directBuffer(initialCapacity); + } + + @Override + public ByteBuf directBuffer(int initialCapacity, int maxCapacity) { + return allocator.directBuffer(initialCapacity, maxCapacity); + } + + @Override + public CompositeByteBuf compositeBuffer() { + return allocator.compositeDirectBuffer(); + } + + @Override + public CompositeByteBuf compositeBuffer(int maxNumComponents) { + return allocator.compositeDirectBuffer(maxNumComponents); + } + + @Override + public CompositeByteBuf compositeHeapBuffer() { + return allocator.compositeHeapBuffer(); + } + + @Override + public CompositeByteBuf compositeHeapBuffer(int maxNumComponents) { + return allocator.compositeHeapBuffer(maxNumComponents); + } + + @Override + public CompositeByteBuf compositeDirectBuffer() { + return allocator.compositeDirectBuffer(); + } + + @Override + public CompositeByteBuf compositeDirectBuffer(int maxNumComponents) { + return allocator.compositeDirectBuffer(maxNumComponents); + } + + @Override + public boolean isDirectBufferPooled() { + return allocator.isDirectBufferPooled(); + } + + @Override + public int calculateNewCapacity(int minNewCapacity, int maxCapacity) { + return allocator.calculateNewCapacity(minNewCapacity, maxCapacity); + } +} From 79706357c73ded02615d0445db7503b646ff9547 Mon Sep 17 00:00:00 2001 From: Terence Yim <885032+chtyim@users.noreply.github.com> Date: Wed, 29 Aug 2018 10:42:01 -0700 Subject: [PATCH 141/417] Fix race condition in the NonStickyEventExecutorGroup (#8232) Motivation: There was a race condition between the task submitter and task executor threads such that the last Runnable submitted may not get executed. Modifications: The bug was fixed by checking the task queue and state in the task executor thread after it saw the task queue was empty. Result: Fixes #8230 --- .../NonStickyEventExecutorGroup.java | 19 +++++++++++- .../NonStickyEventExecutorGroupTest.java | 30 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/io/netty/util/concurrent/NonStickyEventExecutorGroup.java b/common/src/main/java/io/netty/util/concurrent/NonStickyEventExecutorGroup.java index ed3a5873ecae..bcc4b8299640 100644 --- a/common/src/main/java/io/netty/util/concurrent/NonStickyEventExecutorGroup.java +++ b/common/src/main/java/io/netty/util/concurrent/NonStickyEventExecutorGroup.java @@ -259,7 +259,24 @@ public void run() { } } else { state.set(NONE); - return; // done + // After setting the state to NONE, look at the tasks queue one more time. + // If it is empty, then we can return from this method. + // Otherwise, it means the producer thread has called execute(Runnable) + // and enqueued a task in between the tasks.poll() above and the state.set(NONE) here. + // There are two possible scenarios when this happen + // + // 1. The producer thread sees state == NONE, hence the compareAndSet(NONE, SUBMITTED) + // is successfully setting the state to SUBMITTED. This mean the producer + // will call / has called executor.execute(this). In this case, we can just return. + // 2. The producer thread don't see the state change, hence the compareAndSet(NONE, SUBMITTED) + // returns false. In this case, the producer thread won't call executor.execute. + // In this case, we need to change the state to RUNNING and keeps running. + // + // The above cases can be distinguished by performing a + // compareAndSet(NONE, RUNNING). If it returns "false", it is case 1; otherwise it is case 2. + if (tasks.peek() == null || !state.compareAndSet(NONE, RUNNING)) { + return; // done + } } } } diff --git a/common/src/test/java/io/netty/util/concurrent/NonStickyEventExecutorGroupTest.java b/common/src/test/java/io/netty/util/concurrent/NonStickyEventExecutorGroupTest.java index 16035505e8a7..b32df7eb4f7f 100644 --- a/common/src/test/java/io/netty/util/concurrent/NonStickyEventExecutorGroupTest.java +++ b/common/src/test/java/io/netty/util/concurrent/NonStickyEventExecutorGroupTest.java @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -93,6 +94,35 @@ public void run() { } } + @Test + public void testRaceCondition() throws InterruptedException { + EventExecutorGroup group = new UnorderedThreadPoolEventExecutor(1); + NonStickyEventExecutorGroup nonStickyGroup = new NonStickyEventExecutorGroup(group, maxTaskExecutePerRun); + + try { + EventExecutor executor = nonStickyGroup.next(); + + for (int j = 0; j < 5000; j++) { + final CountDownLatch firstCompleted = new CountDownLatch(1); + final CountDownLatch latch = new CountDownLatch(2); + for (int i = 0; i < 2; i++) { + executor.execute(new Runnable() { + @Override + public void run() { + firstCompleted.countDown(); + latch.countDown(); + } + }); + Assert.assertTrue(firstCompleted.await(1, TimeUnit.SECONDS)); + } + + Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); + } + } finally { + nonStickyGroup.shutdownGracefully(); + } + } + private static void execute(EventExecutorGroup group, CountDownLatch startLatch) throws Throwable { EventExecutor executor = group.next(); Assert.assertTrue(executor instanceof OrderedEventExecutor); From ea626ef8c390dc81c71b3a16521cf591611bc5df Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 29 Aug 2018 21:52:26 +0200 Subject: [PATCH 142/417] Log more details when shutdown SSL because of an error. (#8236) Motivation: We should log a bit more details about why we shutdown the SSL. Modifications: Add the return value of SSL_get_error(...) as well in debug mode. Result: More logging to make it easier to understand why an SSL error happened. --- .../ssl/ReferenceCountedOpenSslEngine.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index f902e000b73a..d96450935f9a 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -798,7 +798,7 @@ public final SSLEngineResult wrap( return newResult(BUFFER_OVERFLOW, status, bytesConsumed, bytesProduced); } else { // Everything else is considered as error - throw shutdownWithError("SSL_write"); + throw shutdownWithError("SSL_write", sslError); } } } @@ -855,14 +855,14 @@ private SSLEngineResult newResultMayFinishHandshake(SSLEngineResult.Status statu /** * Log the error, shutdown the engine and throw an exception. */ - private SSLException shutdownWithError(String operations) { + private SSLException shutdownWithError(String operations, int sslError) { String err = SSL.getLastError(); - return shutdownWithError(operations, err); + return shutdownWithError(operations, sslError, err); } - private SSLException shutdownWithError(String operation, String err) { + private SSLException shutdownWithError(String operation, int sslError, String err) { if (logger.isDebugEnabled()) { - logger.debug("{} failed: OpenSSL error: {}", operation, err); + logger.debug("{} failed with {}: OpenSSL error: {}", operation, sslError, err); } // There was an internal error -- shutdown @@ -1074,7 +1074,7 @@ public final SSLEngineResult unwrap( return newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status, bytesConsumed, bytesProduced); } else { - return sslReadErrorResult(SSL.getLastErrorNumber(), bytesConsumed, + return sslReadErrorResult(sslError, SSL.getLastErrorNumber(), bytesConsumed, bytesProduced); } } @@ -1103,7 +1103,8 @@ public final SSLEngineResult unwrap( } } - private SSLEngineResult sslReadErrorResult(int err, int bytesConsumed, int bytesProduced) throws SSLException { + private SSLEngineResult sslReadErrorResult(int error, int stackError, int bytesConsumed, int bytesProduced) + throws SSLException { // Check if we have a pending handshakeException and if so see if we need to consume all pending data from the // BIO first or can just shutdown and throw it now. // This is needed so we ensure close_notify etc is correctly send to the remote peer. @@ -1112,14 +1113,14 @@ private SSLEngineResult sslReadErrorResult(int err, int bytesConsumed, int bytes if (handshakeException == null && handshakeState != HandshakeState.FINISHED) { // we seems to have data left that needs to be transfered and so the user needs // call wrap(...). Store the error so we can pick it up later. - handshakeException = new SSLHandshakeException(SSL.getErrorString(err)); + handshakeException = new SSLHandshakeException(SSL.getErrorString(stackError)); } // We need to clear all errors so we not pick up anything that was left on the stack on the next // operation. Note that shutdownWithError(...) will cleanup the stack as well so its only needed here. SSL.clearError(); return new SSLEngineResult(OK, NEED_WRAP, bytesConsumed, bytesProduced); } - throw shutdownWithError("SSL_read", SSL.getErrorString(err)); + throw shutdownWithError("SSL_read", error, SSL.getErrorString(stackError)); } private void closeAll() throws SSLException { @@ -1588,7 +1589,7 @@ private SSLEngineResult.HandshakeStatus handshake() throws SSLException { return pendingStatus(SSL.bioLengthNonApplication(networkBIO)); } else { // Everything else is considered as error - throw shutdownWithError("SSL_do_handshake"); + throw shutdownWithError("SSL_do_handshake", sslError); } } // if SSL_do_handshake returns > 0 or sslError == SSL.SSL_ERROR_NAME it means the handshake was finished. From b73f785631959647385cd5e546bff3c408c8d891 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 30 Aug 2018 06:56:42 +0200 Subject: [PATCH 143/417] We should call the UnLoad methods when we detect an error during calling OnLoad (#8237) Motivation: We should ensure we call *UnLoad when we detect an error during calling *OnLoad and previous *OnLoad calls were succesfull. Modifications: Correctly call *UnLoad when needed. Result: More correct code and no leaks when an error happens during loading the native lib. --- .../src/main/c/netty_epoll_native.c | 79 +++++++++++++++---- .../src/main/c/netty_kqueue_native.c | 67 +++++++++++++--- 2 files changed, 121 insertions(+), 25 deletions(-) diff --git a/transport-native-epoll/src/main/c/netty_epoll_native.c b/transport-native-epoll/src/main/c/netty_epoll_native.c index 64ef61feab10..e6a38b4f233a 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_native.c +++ b/transport-native-epoll/src/main/c/netty_epoll_native.c @@ -420,13 +420,20 @@ static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) { // JNI Method Registration Table End static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { + int limitsOnLoadCalled = 0; + int errorsOnLoadCalled = 0; + int filedescriptorOnLoadCalled = 0; + int socketOnLoadCalled = 0; + int bufferOnLoadCalled = 0; + int linuxsocketOnLoadCalled = 0; + // We must register the statically referenced methods first! if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/epoll/NativeStaticallyReferencedJniMethods", statically_referenced_fixed_method_table, statically_referenced_fixed_method_table_size) != 0) { - return JNI_ERR; + goto error; } // Register the methods which are not referenced by static member variables JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix); @@ -436,29 +443,40 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix dynamicMethods, dynamicMethodsTableSize()) != 0) { freeDynamicMethodsTable(dynamicMethods); - return JNI_ERR; + goto error; } freeDynamicMethodsTable(dynamicMethods); dynamicMethods = NULL; // Load all c modules that we depend upon if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + limitsOnLoadCalled = 1; + if (netty_unix_errors_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + errorsOnLoadCalled = 1; + if (netty_unix_filedescriptor_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + filedescriptorOnLoadCalled = 1; + if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + socketOnLoadCalled = 1; + if (netty_unix_buffer_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + bufferOnLoadCalled = 1; + if (netty_epoll_linuxsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + linuxsocketOnLoadCalled = 1; // Initialize this module char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket"); @@ -467,37 +485,64 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix nettyClassName = NULL; if (nativeDatagramPacketCls == NULL) { // pending exception... - return JNI_ERR; + goto error; } packetAddrFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "addr", "[B"); if (packetAddrFieldId == NULL) { netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.addr"); - return JNI_ERR; + goto error; } packetScopeIdFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "scopeId", "I"); if (packetScopeIdFieldId == NULL) { netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.scopeId"); - return JNI_ERR; + goto error; } packetPortFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "port", "I"); if (packetPortFieldId == NULL) { netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.port"); - return JNI_ERR; + goto error; } packetMemoryAddressFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "memoryAddress", "J"); if (packetMemoryAddressFieldId == NULL) { netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.memoryAddress"); - return JNI_ERR; + goto error; } packetCountFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "count", "I"); if (packetCountFieldId == NULL) { netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.count"); - return JNI_ERR; + goto error; } return NETTY_JNI_VERSION; + +error: + if (limitsOnLoadCalled == 1) { + netty_unix_limits_JNI_OnUnLoad(env); + } + if (errorsOnLoadCalled == 1) { + netty_unix_errors_JNI_OnUnLoad(env); + } + if (filedescriptorOnLoadCalled == 1) { + netty_unix_filedescriptor_JNI_OnUnLoad(env); + } + if (socketOnLoadCalled == 1) { + netty_unix_socket_JNI_OnUnLoad(env); + } + if (bufferOnLoadCalled == 1) { + netty_unix_buffer_JNI_OnUnLoad(env); + } + if (linuxsocketOnLoadCalled == 1) { + netty_epoll_linuxsocket_JNI_OnUnLoad(env); + } + packetAddrFieldId = NULL; + packetScopeIdFieldId = NULL; + packetPortFieldId = NULL; + packetMemoryAddressFieldId = NULL; + packetCountFieldId = NULL; + + return JNI_ERR; } static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) { @@ -507,6 +552,12 @@ static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) { netty_unix_socket_JNI_OnUnLoad(env); netty_unix_buffer_JNI_OnUnLoad(env); netty_epoll_linuxsocket_JNI_OnUnLoad(env); + + packetAddrFieldId = NULL; + packetScopeIdFieldId = NULL; + packetPortFieldId = NULL; + packetMemoryAddressFieldId = NULL; + packetCountFieldId = NULL; } // Invoked by the JVM when statically linked diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_native.c b/transport-native-kqueue/src/main/c/netty_kqueue_native.c index 82222c80cc71..a2a5a874a23d 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_native.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_native.c @@ -269,48 +269,93 @@ static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof( // JNI Method Registration Table End static jint netty_kqueue_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { + int limitsOnLoadCalled = 0; + int errorsOnLoadCalled = 0; + int filedescriptorOnLoadCalled = 0; + int socketOnLoadCalled = 0; + int bufferOnLoadCalled = 0; + int bsdsocketOnLoadCalled = 0; + int eventarrayOnLoadCalled = 0; + // We must register the statically referenced methods first! if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods", statically_referenced_fixed_method_table, statically_referenced_fixed_method_table_size) != 0) { - return JNI_ERR; + goto error; } // Register the methods which are not referenced by static member variables if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/kqueue/Native", fixed_method_table, fixed_method_table_size) != 0) { - return JNI_ERR; + goto error; } // Load all c modules that we depend upon if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + limitsOnLoadCalled = 1; + if (netty_unix_errors_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + errorsOnLoadCalled = 1; + if (netty_unix_filedescriptor_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + filedescriptorOnLoadCalled = 1; + if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + socketOnLoadCalled = 1; + if (netty_unix_buffer_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + bufferOnLoadCalled = 1; + if (netty_kqueue_bsdsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + bsdsocketOnLoadCalled = 1; + if (netty_kqueue_eventarray_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - return JNI_ERR; + goto error; } + eventarrayOnLoadCalled = 1; + // Initialize this module if (!netty_unix_util_initialize_wait_clock(&waitClockId)) { - fprintf(stderr, "FATAL: could not find a clock for clock_gettime!\n"); - return JNI_ERR; + fprintf(stderr, "FATAL: could not find a clock for clock_gettime!\n"); + goto error; } return NETTY_JNI_VERSION; +error: + if (limitsOnLoadCalled == 1) { + netty_unix_limits_JNI_OnUnLoad(env); + } + if (errorsOnLoadCalled == 1) { + netty_unix_errors_JNI_OnUnLoad(env); + } + if (filedescriptorOnLoadCalled == 1) { + netty_unix_filedescriptor_JNI_OnUnLoad(env); + } + if (socketOnLoadCalled == 1) { + netty_unix_socket_JNI_OnUnLoad(env); + } + if (bufferOnLoadCalled == 1) { + netty_unix_buffer_JNI_OnUnLoad(env); + } + if (bsdsocketOnLoadCalled == 1) { + netty_kqueue_bsdsocket_JNI_OnUnLoad(env); + } + if (eventarrayOnLoadCalled == 1) { + netty_kqueue_eventarray_JNI_OnUnLoad(env); + } + return JNI_ERR; } static void netty_kqueue_native_JNI_OnUnLoad(JNIEnv* env) { From 4a5b61fc13022d0c2cddc93258f303b1a4948cfe Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 30 Aug 2018 06:57:12 +0200 Subject: [PATCH 144/417] Fix log message about using non-direct buffers by default (#8235) Motivation: f77891cc1786806630f6c4408d5d37abb1891e7b changed slightly how we detect if we should prefer direct buffers or not but did miss to also take this into account when logging. Modifications: Fix branch for log message to reflect changes in f77891cc1786806630f6c4408d5d37abb1891e7b. Result: Correct logging. --- .../util/internal/PlatformDependent.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index 6a534bcff0af..8d44920cc237 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -128,17 +128,6 @@ public Random current() { }; } - /* - * We do not want to log this message if unsafe is explicitly disabled. Do not remove the explicit no unsafe - * guard. - */ - if (!hasUnsafe() && !isAndroid() && !PlatformDependent0.isExplicitNoUnsafe()) { - logger.info( - "Your platform does not provide complete low-level API for accessing direct buffers reliably. " + - "Unless explicitly requested, heap buffer will always be preferred to avoid potential system " + - "instability."); - } - // Here is how the system property is used: // // * < 0 - Don't use cleaner, and inherit max direct memory from java. In this case the @@ -193,6 +182,17 @@ public Random current() { if (logger.isDebugEnabled()) { logger.debug("-Dio.netty.noPreferDirect: {}", !DIRECT_BUFFER_PREFERRED); } + + /* + * We do not want to log this message if unsafe is explicitly disabled. Do not remove the explicit no unsafe + * guard. + */ + if (CLEANER == NOOP && !PlatformDependent0.isExplicitNoUnsafe()) { + logger.info( + "Your platform does not provide complete low-level API for accessing direct buffers reliably. " + + "Unless explicitly requested, heap buffer will always be preferred to avoid potential system " + + "instability."); + } } public static boolean hasDirectBufferNoCleanerConstructor() { From 38eee409c8ca356d4a0254919e5898ecd9a2100c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 30 Aug 2018 07:43:10 +0200 Subject: [PATCH 145/417] =?UTF-8?q?We=20should=20be=20able=20to=20use=20th?= =?UTF-8?q?e=20ByteBuffer=20cleaner=20on=20java8=20(and=20earlier=E2=80=A6?= =?UTF-8?q?=20(#8234)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * We should be able to use the ByteBuffer cleaner on java8 (and earlier versions) even if sun.misc.Unsafe is not present. Motivation: At the moment we have a hard dependency on sun.misc.Unsafe to use the Cleaner on Java8 and earlier. This is not really needed as we can still use pure reflection if sun.misc.Unsafe is not present. Modifications: Refactor Cleaner6 to fallback to pure reflection if sun.misc.Unsafe is not present on system. Result: More timely releasing of direct memory on Java8 and earlier when sun.misc.Unsafe is not present. --- .../io/netty/util/internal/CleanerJava6.java | 107 ++++++++++++++---- .../util/internal/PlatformDependent.java | 2 +- 2 files changed, 87 insertions(+), 22 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/CleanerJava6.java b/common/src/main/java/io/netty/util/internal/CleanerJava6.java index 8bbdbf724016..383582360332 100644 --- a/common/src/main/java/io/netty/util/internal/CleanerJava6.java +++ b/common/src/main/java/io/netty/util/internal/CleanerJava6.java @@ -21,6 +21,8 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; /** @@ -32,41 +34,72 @@ final class CleanerJava6 implements Cleaner { private static final long CLEANER_FIELD_OFFSET; private static final Method CLEAN_METHOD; + private static final Field CLEANER_FIELD; private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava6.class); static { - long fieldOffset = -1; - Method clean = null; + long fieldOffset; + Method clean; + Field cleanerField; Throwable error = null; - if (PlatformDependent0.hasUnsafe()) { - ByteBuffer direct = ByteBuffer.allocateDirect(1); - try { - Field cleanerField = direct.getClass().getDeclaredField("cleaner"); + final ByteBuffer direct = ByteBuffer.allocateDirect(1); + try { + Object mayBeCleanerField = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + try { + Field cleanerField = direct.getClass().getDeclaredField("cleaner"); + if (!PlatformDependent.hasUnsafe()) { + // We need to make it accessible if we do not use Unsafe as we will access it via + // reflection. + cleanerField.setAccessible(true); + } + return cleanerField; + } catch (Throwable cause) { + return cause; + } + } + }); + if (mayBeCleanerField instanceof Throwable) { + throw (Throwable) mayBeCleanerField; + } + + cleanerField = (Field) mayBeCleanerField; + + final Object cleaner; + + // If we have sun.misc.Unsafe we will use it as its faster then using reflection, + // otherwise let us try reflection as last resort. + if (PlatformDependent.hasUnsafe()) { fieldOffset = PlatformDependent0.objectFieldOffset(cleanerField); - Object cleaner = PlatformDependent0.getObject(direct, fieldOffset); - clean = cleaner.getClass().getDeclaredMethod("clean"); - clean.invoke(cleaner); - } catch (Throwable t) { - // We don't have ByteBuffer.cleaner(). + cleaner = PlatformDependent0.getObject(direct, fieldOffset); + } else { fieldOffset = -1; - clean = null; - error = t; + cleaner = cleanerField.get(direct); } - } else { - error = new UnsupportedOperationException("sun.misc.Unsafe unavailable"); + clean = cleaner.getClass().getDeclaredMethod("clean"); + clean.invoke(cleaner); + } catch (Throwable t) { + // We don't have ByteBuffer.cleaner(). + fieldOffset = -1; + clean = null; + error = t; + cleanerField = null; } + if (error == null) { logger.debug("java.nio.ByteBuffer.cleaner(): available"); } else { logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error); } + CLEANER_FIELD = cleanerField; CLEANER_FIELD_OFFSET = fieldOffset; CLEAN_METHOD = clean; } static boolean isSupported() { - return CLEANER_FIELD_OFFSET != -1; + return CLEANER_FIELD_OFFSET != -1 || CLEANER_FIELD != null; } @Override @@ -74,13 +107,45 @@ public void freeDirectBuffer(ByteBuffer buffer) { if (!buffer.isDirect()) { return; } - try { - Object cleaner = PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET); - if (cleaner != null) { - CLEAN_METHOD.invoke(cleaner); + if (System.getSecurityManager() == null) { + try { + freeDirectBuffer0(buffer); + } catch (Throwable cause) { + PlatformDependent0.throwException(cause); } - } catch (Throwable cause) { + } else { + freeDirectBufferPrivileged(buffer); + } + } + + private static void freeDirectBufferPrivileged(final ByteBuffer buffer) { + Throwable cause = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Throwable run() { + try { + freeDirectBuffer0(buffer); + return null; + } catch (Throwable cause) { + return cause; + } + } + }); + if (cause != null) { PlatformDependent0.throwException(cause); } } + + private static void freeDirectBuffer0(ByteBuffer buffer) throws Exception { + final Object cleaner; + // If CLEANER_FIELD_OFFSET == -1 we need to use reflection to access the cleaner, otherwise we can use + // sun.misc.Unsafe. + if (CLEANER_FIELD_OFFSET == -1) { + cleaner = CLEANER_FIELD.get(buffer); + } else { + cleaner = PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET); + } + if (cleaner != null) { + CLEAN_METHOD.invoke(cleaner); + } + } } diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index 8d44920cc237..64d740aaf34e 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -164,7 +164,7 @@ public Random current() { MAYBE_SUPER_USER = maybeSuperUser0(); - if (!isAndroid() && hasUnsafe()) { + if (!isAndroid()) { // only direct to method if we are not running on android. // See https://github.com/netty/netty/issues/2604 if (javaVersion() >= 9) { From a64456362589aabe90001c36db743b27c91db797 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 30 Aug 2018 20:44:47 +0200 Subject: [PATCH 146/417] Add more debug informations when log SSL errors. (#8241) Motivation: ea626ef8c390dc81c71b3a16521cf591611bc5df added more debug logging but we can even include a bit more. Modifications: Always log the error number as well. Result: More informations for debugging SSL errors. --- .../ssl/ReferenceCountedOpenSslEngine.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index d96450935f9a..597fc5079c47 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -856,21 +856,22 @@ private SSLEngineResult newResultMayFinishHandshake(SSLEngineResult.Status statu * Log the error, shutdown the engine and throw an exception. */ private SSLException shutdownWithError(String operations, int sslError) { - String err = SSL.getLastError(); - return shutdownWithError(operations, sslError, err); + return shutdownWithError(operations, sslError, SSL.getLastErrorNumber()); } - private SSLException shutdownWithError(String operation, int sslError, String err) { + private SSLException shutdownWithError(String operation, int sslError, int error) { + String errorString = SSL.getErrorString(error); if (logger.isDebugEnabled()) { - logger.debug("{} failed with {}: OpenSSL error: {}", operation, sslError, err); + logger.debug("{} failed with {}: OpenSSL error: {} {}", + operation, sslError, error, errorString); } // There was an internal error -- shutdown shutdown(); if (handshakeState == HandshakeState.FINISHED) { - return new SSLException(err); + return new SSLException(errorString); } - return new SSLHandshakeException(err); + return new SSLHandshakeException(errorString); } public final SSLEngineResult unwrap( @@ -1120,7 +1121,7 @@ private SSLEngineResult sslReadErrorResult(int error, int stackError, int bytesC SSL.clearError(); return new SSLEngineResult(OK, NEED_WRAP, bytesConsumed, bytesProduced); } - throw shutdownWithError("SSL_read", error, SSL.getErrorString(stackError)); + throw shutdownWithError("SSL_read", error, stackError); } private void closeAll() throws SSLException { @@ -1269,7 +1270,8 @@ private boolean doSSLShutdown() { int sslErr = SSL.getError(ssl, err); if (sslErr == SSL.SSL_ERROR_SYSCALL || sslErr == SSL.SSL_ERROR_SSL) { if (logger.isDebugEnabled()) { - logger.debug("SSL_shutdown failed: OpenSSL error: {}", SSL.getLastError()); + int error = SSL.getLastErrorNumber(); + logger.debug("SSL_shutdown failed: OpenSSL error: {} {}", error, SSL.getErrorString(error)); } // There was an internal error -- shutdown shutdown(); From a9863f8128189098263ebfe42359d8c6d53f4b4e Mon Sep 17 00:00:00 2001 From: Chi-Joung So Date: Fri, 31 Aug 2018 15:06:09 +0200 Subject: [PATCH 147/417] Add headers to MqttMessage returned by MqttDecoder in case of DecoderException (#8219) Motivation: When the MqttDecoder decodes a message larger than the 'maxBytesInMessage' a DecoderException is thrown and a MqttMessage with just the failure cause is returned. Even if I can't handle the message, I might want to send an ACK so that I won't have to worry about it again. Modification: The DecoderException is thrown after the variableHeader is decoded. The fixed and variable headers are then added to the MqttMessage along with the failure cause. Result: The invalid MqttMessage will have headers if available. --- .../netty/handler/codec/mqtt/MqttDecoder.java | 6 +- .../codec/mqtt/MqttMessageFactory.java | 5 + .../handler/codec/mqtt/MqttCodecTest.java | 167 +++++++++++++++++- 3 files changed, 173 insertions(+), 5 deletions(-) diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttDecoder.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttDecoder.java index e014f37ac2bd..e93d34bde155 100644 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttDecoder.java +++ b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttDecoder.java @@ -82,11 +82,11 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List ou } case READ_VARIABLE_HEADER: try { + final Result decodedVariableHeader = decodeVariableHeader(buffer, mqttFixedHeader); + variableHeader = decodedVariableHeader.value; if (bytesRemainingInVariablePart > maxBytesInMessage) { throw new DecoderException("too large message: " + bytesRemainingInVariablePart + " bytes"); } - final Result decodedVariableHeader = decodeVariableHeader(buffer, mqttFixedHeader); - variableHeader = decodedVariableHeader.value; bytesRemainingInVariablePart -= decodedVariableHeader.numberOfBytesConsumed; checkpoint(DecoderState.READ_PAYLOAD); // fall through @@ -133,7 +133,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List ou private MqttMessage invalidMessage(Throwable cause) { checkpoint(DecoderState.BAD_MESSAGE); - return MqttMessageFactory.newInvalidMessage(cause); + return MqttMessageFactory.newInvalidMessage(mqttFixedHeader, variableHeader, cause); } /** diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageFactory.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageFactory.java index 69f07c07273c..09f42698d781 100644 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageFactory.java +++ b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageFactory.java @@ -85,5 +85,10 @@ public static MqttMessage newInvalidMessage(Throwable cause) { return new MqttMessage(null, null, null, DecoderResult.failure(cause)); } + public static MqttMessage newInvalidMessage(MqttFixedHeader mqttFixedHeader, Object variableHeader, + Throwable cause) { + return new MqttMessage(mqttFixedHeader, variableHeader, null, DecoderResult.failure(cause)); + } + private MqttMessageFactory() { } } diff --git a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttCodecTest.java b/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttCodecTest.java index f69387ef2166..927334cb1d86 100644 --- a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttCodecTest.java +++ b/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttCodecTest.java @@ -58,6 +58,11 @@ public class MqttCodecTest { private final MqttDecoder mqttDecoder = new MqttDecoder(); + /** + * MqttDecoder with an unrealistic max payload size of 1 byte. + */ + private final MqttDecoder mqttDecoderLimitedMessageSize = new MqttDecoder(1); + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -297,6 +302,154 @@ public void testUnknownMessageType() throws Exception { } } + @Test + public void testConnectMessageForMqtt31TooLarge() throws Exception { + final MqttConnectMessage message = createConnectMessage(MqttVersion.MQTT_3_1); + ByteBuf byteBuf = MqttEncoder.doEncode(ALLOCATOR, message); + + try { + final List out = new LinkedList(); + mqttDecoderLimitedMessageSize.decode(ctx, byteBuf, out); + + assertEquals("Expected one object but got " + out.size(), 1, out.size()); + + final MqttMessage decodedMessage = (MqttMessage) out.get(0); + + validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); + validateConnectVariableHeader(message.variableHeader(), + (MqttConnectVariableHeader) decodedMessage.variableHeader()); + validateDecoderExceptionTooLargeMessage(decodedMessage); + } finally { + byteBuf.release(); + } + } + + @Test + public void testConnectMessageForMqtt311TooLarge() throws Exception { + final MqttConnectMessage message = createConnectMessage(MqttVersion.MQTT_3_1_1); + ByteBuf byteBuf = MqttEncoder.doEncode(ALLOCATOR, message); + + try { + final List out = new LinkedList(); + mqttDecoderLimitedMessageSize.decode(ctx, byteBuf, out); + + assertEquals("Expected one object but got " + out.size(), 1, out.size()); + + final MqttMessage decodedMessage = (MqttMessage) out.get(0); + + validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); + validateConnectVariableHeader(message.variableHeader(), + (MqttConnectVariableHeader) decodedMessage.variableHeader()); + validateDecoderExceptionTooLargeMessage(decodedMessage); + } finally { + byteBuf.release(); + } + } + + @Test + public void testConnAckMessageTooLarge() throws Exception { + final MqttConnAckMessage message = createConnAckMessage(); + ByteBuf byteBuf = MqttEncoder.doEncode(ALLOCATOR, message); + + try { + final List out = new LinkedList(); + mqttDecoderLimitedMessageSize.decode(ctx, byteBuf, out); + + assertEquals("Expected one object but got " + out.size(), 1, out.size()); + + final MqttMessage decodedMessage = (MqttMessage) out.get(0); + validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); + validateDecoderExceptionTooLargeMessage(decodedMessage); + } finally { + byteBuf.release(); + } + } + + @Test + public void testPublishMessageTooLarge() throws Exception { + final MqttPublishMessage message = createPublishMessage(); + ByteBuf byteBuf = MqttEncoder.doEncode(ALLOCATOR, message); + + try { + final List out = new LinkedList(); + mqttDecoderLimitedMessageSize.decode(ctx, byteBuf, out); + + assertEquals("Expected one object but got " + out.size(), 1, out.size()); + + final MqttMessage decodedMessage = (MqttMessage) out.get(0); + + validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); + validatePublishVariableHeader(message.variableHeader(), + (MqttPublishVariableHeader) decodedMessage.variableHeader()); + validateDecoderExceptionTooLargeMessage(decodedMessage); + } finally { + byteBuf.release(); + } + } + + @Test + public void testSubscribeMessageTooLarge() throws Exception { + final MqttSubscribeMessage message = createSubscribeMessage(); + ByteBuf byteBuf = MqttEncoder.doEncode(ALLOCATOR, message); + + try { + final List out = new LinkedList(); + mqttDecoderLimitedMessageSize.decode(ctx, byteBuf, out); + + assertEquals("Expected one object but got " + out.size(), 1, out.size()); + + final MqttMessage decodedMessage = (MqttMessage) out.get(0); + validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); + validateMessageIdVariableHeader(message.variableHeader(), + (MqttMessageIdVariableHeader) decodedMessage.variableHeader()); + validateDecoderExceptionTooLargeMessage(decodedMessage); + } finally { + byteBuf.release(); + } + } + + @Test + public void testSubAckMessageTooLarge() throws Exception { + final MqttSubAckMessage message = createSubAckMessage(); + ByteBuf byteBuf = MqttEncoder.doEncode(ALLOCATOR, message); + + try { + final List out = new LinkedList(); + mqttDecoderLimitedMessageSize.decode(ctx, byteBuf, out); + + assertEquals("Expected one object but got " + out.size(), 1, out.size()); + + final MqttMessage decodedMessage = (MqttMessage) out.get(0); + validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); + validateMessageIdVariableHeader(message.variableHeader(), + (MqttMessageIdVariableHeader) decodedMessage.variableHeader()); + validateDecoderExceptionTooLargeMessage(decodedMessage); + } finally { + byteBuf.release(); + } + } + + @Test + public void testUnSubscribeMessageTooLarge() throws Exception { + final MqttUnsubscribeMessage message = createUnsubscribeMessage(); + ByteBuf byteBuf = MqttEncoder.doEncode(ALLOCATOR, message); + + try { + final List out = new LinkedList(); + mqttDecoderLimitedMessageSize.decode(ctx, byteBuf, out); + + assertEquals("Expected one object but got " + out.size(), 1, out.size()); + + final MqttMessage decodedMessage = (MqttMessage) out.get(0); + validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); + validateMessageIdVariableHeader(message.variableHeader(), + (MqttMessageIdVariableHeader) decodedMessage.variableHeader()); + validateDecoderExceptionTooLargeMessage(decodedMessage); + } finally { + byteBuf.release(); + } + } + private void testMessageWithOnlyFixedHeader(MqttMessageType messageType) throws Exception { MqttMessage message = createMessageWithFixedHeader(messageType); ByteBuf byteBuf = MqttEncoder.doEncode(ALLOCATOR, message); @@ -340,7 +493,7 @@ private static MqttMessage createMessageWithFixedHeaderAndMessageIdVariableHeade new MqttFixedHeader( messageType, false, - messageType == MqttMessageType.PUBREL ? MqttQoS.AT_LEAST_ONCE : MqttQoS.AT_MOST_ONCE, + messageType == MqttMessageType.PUBREL ? MqttQoS.AT_LEAST_ONCE : MqttQoS.AT_MOST_ONCE, false, 0); MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(12345); @@ -378,7 +531,7 @@ private static MqttPublishMessage createPublishMessage() { MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_LEAST_ONCE, true, 0); MqttPublishVariableHeader mqttPublishVariableHeader = new MqttPublishVariableHeader("/abc", 1234); - ByteBuf payload = ALLOCATOR.buffer(); + ByteBuf payload = ALLOCATOR.buffer(); payload.writeBytes("whatever".getBytes(CharsetUtil.UTF_8)); return new MqttPublishMessage(mqttFixedHeader, mqttPublishVariableHeader, payload); } @@ -530,4 +683,14 @@ private static void validateUnsubscribePayload(MqttUnsubscribePayload expected, expected.topics().toArray(), actual.topics().toArray()); } + + private static void validateDecoderExceptionTooLargeMessage(MqttMessage message) { + assertNull("MqttMessage payload expected null ", message.payload()); + assertTrue(message.decoderResult().isFailure()); + Throwable cause = message.decoderResult().cause(); + assertTrue("MqttMessage DecoderResult cause expected instance of DecoderException ", + cause instanceof DecoderException); + assertTrue("MqttMessage DecoderResult cause reason expect to contain 'too large message' ", + cause.getMessage().contains("too large message:")); + } } From e26666a7ea7774ea2f7cf25b812e270b5410f3b6 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 31 Aug 2018 17:08:14 +0200 Subject: [PATCH 148/417] Update to latest netty-tcnative (#8246) Motivation: We should use the latest netty-tcnative release which contains a fix to correctly support DH based ciphers when using openssl 1.1.x Modifications: Update to latest netty-tcnative which has the fix. Result: Correctly support DH ciphers in all cases. Fixes https://github.com/netty/netty/issues/8165. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d9a15597cb1e..b30b9545ae3b 100644 --- a/pom.xml +++ b/pom.xml @@ -221,7 +221,7 @@ fedora netty-tcnative - 2.0.14.Final + 2.0.15.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber From 9d8846cfce4daa9750ab822c91e5a37bc44906eb Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 31 Aug 2018 17:08:38 +0200 Subject: [PATCH 149/417] Cleanup Log4J2Logger (#8245) Motivation: Log4J2Logger had some code-duplication with AbstractInternalLogger Modifications: Reuse AbstractInternaLogger.EXCEPTION_MESSAGE in Log4J2Logger and so remove some code-duplication Result: Less duplicated code. --- .../logging/AbstractInternalLogger.java | 2 +- .../util/internal/logging/Log4J2Logger.java | 7 +++---- .../internal/logging/Log4J2LoggerTest.java | 21 ------------------- 3 files changed, 4 insertions(+), 26 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/logging/AbstractInternalLogger.java b/common/src/main/java/io/netty/util/internal/logging/AbstractInternalLogger.java index 78007c1e4a9a..6486efaf088f 100644 --- a/common/src/main/java/io/netty/util/internal/logging/AbstractInternalLogger.java +++ b/common/src/main/java/io/netty/util/internal/logging/AbstractInternalLogger.java @@ -29,7 +29,7 @@ public abstract class AbstractInternalLogger implements InternalLogger, Serializ private static final long serialVersionUID = -6382972526573193470L; - private static final String EXCEPTION_MESSAGE = "Unexpected exception:"; + static final String EXCEPTION_MESSAGE = "Unexpected exception:"; private final String name; diff --git a/common/src/main/java/io/netty/util/internal/logging/Log4J2Logger.java b/common/src/main/java/io/netty/util/internal/logging/Log4J2Logger.java index 8ed2fdf48df6..36f28083753b 100644 --- a/common/src/main/java/io/netty/util/internal/logging/Log4J2Logger.java +++ b/common/src/main/java/io/netty/util/internal/logging/Log4J2Logger.java @@ -21,13 +21,12 @@ import org.apache.logging.log4j.spi.ExtendedLogger; import org.apache.logging.log4j.spi.ExtendedLoggerWrapper; +import static io.netty.util.internal.logging.AbstractInternalLogger.EXCEPTION_MESSAGE; + class Log4J2Logger extends ExtendedLoggerWrapper implements InternalLogger { private static final long serialVersionUID = 5485418394879791397L; - /** {@linkplain AbstractInternalLogger#EXCEPTION_MESSAGE} */ - private static final String EXCEPTION_MESSAGE = "Unexpected exception:"; - Log4J2Logger(Logger logger) { super((ExtendedLogger) logger, logger.getName(), logger.getMessageFactory()); } @@ -97,7 +96,7 @@ public void log(InternalLogLevel level, Throwable t) { log(toLevel(level), EXCEPTION_MESSAGE, t); } - protected Level toLevel(InternalLogLevel level) { + private static Level toLevel(InternalLogLevel level) { switch (level) { case INFO: return Level.INFO; diff --git a/common/src/test/java/io/netty/util/internal/logging/Log4J2LoggerTest.java b/common/src/test/java/io/netty/util/internal/logging/Log4J2LoggerTest.java index b561993ebd83..a72eb9a2bd4a 100644 --- a/common/src/test/java/io/netty/util/internal/logging/Log4J2LoggerTest.java +++ b/common/src/test/java/io/netty/util/internal/logging/Log4J2LoggerTest.java @@ -17,7 +17,6 @@ import static org.junit.Assert.assertEquals; -import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; @@ -29,7 +28,6 @@ import org.apache.logging.log4j.spi.ExtendedLoggerWrapper; import org.hamcrest.CoreMatchers; import org.junit.Assume; -import org.junit.Test; import io.netty.util.internal.ReflectionUtil; @@ -56,25 +54,6 @@ public void logMessage(String fqcn, Level level, Marker marker, Message message, }; } - @Test - public void testEXCEPTION_MESSAGE() { - assertEquals(getFieldValue(AbstractInternalLogger.class, "EXCEPTION_MESSAGE"), - getFieldValue(Log4J2Logger.class, "EXCEPTION_MESSAGE")); - } - - @SuppressWarnings("unchecked") - private static T getFieldValue(Class clazz, String fieldName) { - try { - Field field = clazz.getDeclaredField(fieldName); - if (!field.isAccessible()) { - Assume.assumeThat(ReflectionUtil.trySetAccessible(field, true), CoreMatchers.nullValue()); - } - return (T) field.get(AbstractInternalLogger.class); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException(e); - } - } - @Override protected void setLevelEnable(InternalLogLevel level, boolean enable) throws Exception { Level targetLevel = Level.valueOf(level.name()); From c1a335446daf5892eab2e134538ba0388162f18b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 1 Sep 2018 08:10:02 +0200 Subject: [PATCH 150/417] Correctly implement SelectedSelectionKeySet iterator(), contains(...) and remove(...) (#8244) Motivation: Our SelectedSelectionKeySet does not correctly implement various methods which can be done without any performance overhead. Modifications: Implement iterator(), contains(...) and remove(...) Result: Related to https://github.com/netty/netty/issues/8242. --- .../channel/nio/SelectedSelectionKeySet.java | 26 +++++--- .../nio/SelectedSelectionKeySetTest.java | 61 +++++++++++++++++-- 2 files changed, 72 insertions(+), 15 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java b/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java index 534e27f6c778..452f0f0d77c6 100644 --- a/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java +++ b/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java @@ -19,6 +19,7 @@ import java.util.AbstractSet; import java.util.Arrays; import java.util.Iterator; +import java.util.NoSuchElementException; final class SelectedSelectionKeySet extends AbstractSet { @@ -49,18 +50,23 @@ public int size() { } @Override - public boolean remove(Object o) { - return false; - } + public Iterator iterator() { + return new Iterator() { + private int idx; - @Override - public boolean contains(Object o) { - return false; - } + @Override + public boolean hasNext() { + return idx < size; + } - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); + @Override + public SelectionKey next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return keys[idx++]; + } + }; } void reset() { diff --git a/transport/src/test/java/io/netty/channel/nio/SelectedSelectionKeySetTest.java b/transport/src/test/java/io/netty/channel/nio/SelectedSelectionKeySetTest.java index 5c32001b8ed7..83506406925e 100644 --- a/transport/src/test/java/io/netty/channel/nio/SelectedSelectionKeySetTest.java +++ b/transport/src/test/java/io/netty/channel/nio/SelectedSelectionKeySetTest.java @@ -21,12 +21,10 @@ import org.mockito.MockitoAnnotations; import java.nio.channels.SelectionKey; +import java.util.Iterator; +import java.util.NoSuchElementException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class SelectedSelectionKeySetTest { @Mock @@ -34,6 +32,9 @@ public class SelectedSelectionKeySetTest { @Mock private SelectionKey mockKey2; + @Mock + private SelectionKey mockKey3; + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -63,4 +64,54 @@ public void resetSet() { assertEquals(0, set.size()); assertTrue(set.isEmpty()); } + + @Test + public void iterator() { + SelectedSelectionKeySet set = new SelectedSelectionKeySet(); + assertTrue(set.add(mockKey)); + assertTrue(set.add(mockKey2)); + Iterator keys = set.iterator(); + assertTrue(keys.hasNext()); + assertSame(mockKey, keys.next()); + assertTrue(keys.hasNext()); + assertSame(mockKey2, keys.next()); + assertFalse(keys.hasNext()); + + try { + keys.next(); + fail(); + } catch (NoSuchElementException expected) { + // expected + } + + try { + keys.remove(); + fail(); + } catch (UnsupportedOperationException expected) { + // expected + } + } + + @Test + public void contains() { + SelectedSelectionKeySet set = new SelectedSelectionKeySet(); + assertTrue(set.add(mockKey)); + assertTrue(set.add(mockKey2)); + assertTrue(set.contains(mockKey)); + assertTrue(set.contains(mockKey2)); + assertFalse(set.contains(mockKey3)); + } + + @Test + public void remove() { + SelectedSelectionKeySet set = new SelectedSelectionKeySet(); + assertTrue(set.add(mockKey)); + assertFalse(set.remove(mockKey2)); + try { + set.remove(mockKey); + fail(); + } catch (UnsupportedOperationException expected) { + // expected + } + } } From f4bafd4fe0a9fa71452d63eeb5cf9e5be2cb722e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 1 Sep 2018 08:11:13 +0200 Subject: [PATCH 151/417] Correctly check if cipher is supported for each SslProvider before trying to run test and fix buffer leaks in test. (#8247) Motivation: 5aaa16b24cee9d7455ea1cbb4cb9f2e43eb931b7 introduced a testcase for specific ciphersuites and checked if these are supported by our native implementation before running it. Unfortunally this is not good enough as even on the JDK it may not be supported on various JDK versions (like Java7). Beside this the test leaked buffers. Modifications: - Correctly check if ciphersuite is supported on each SslProvider before trying to run test. - Fix buffer leaks. Result: Testsuite pass again on Java7 and others when -Pleak is used. --- .../handler/ssl/CipherSuiteCanaryTest.java | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java index 69837d292d42..22e5f43bf880 100644 --- a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java @@ -18,16 +18,15 @@ import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.DefaultEventLoopGroup; import io.netty.channel.EventLoopGroup; +import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; import io.netty.channel.local.LocalServerChannel; @@ -37,9 +36,10 @@ import io.netty.util.concurrent.Promise; import java.net.SocketAddress; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -51,6 +51,9 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; + import static org.junit.Assert.assertTrue; /** @@ -94,11 +97,30 @@ public CipherSuiteCanaryTest(SslProvider serverSslProvider, SslProvider clientSs this.rfcCipherName = rfcCipherName; } + private static void assumeCipherAvailable(SslProvider provider, String cipher) throws NoSuchAlgorithmException { + boolean cipherSupported = false; + if (provider == SslProvider.JDK) { + SSLEngine engine = SSLContext.getDefault().createSSLEngine(); + for (String c: engine.getSupportedCipherSuites()) { + if (cipher.equals(c)) { + cipherSupported = true; + break; + } + } + } else { + cipherSupported = OpenSsl.isCipherSuiteAvailable(cipher); + } + Assume.assumeTrue("Unsupported cipher: " + cipher, cipherSupported); + } + @Test public void testHandshake() throws Exception { - Assume.assumeTrue("Unsupported cipher: " + rfcCipherName, OpenSsl.isCipherSuiteAvailable(rfcCipherName)); + // Check if the cipher is supported at all which may not be the case for various JDK versions and OpenSSL API + // implementations. + assumeCipherAvailable(serverSslProvider, rfcCipherName); + assumeCipherAvailable(clientSslProvider, rfcCipherName); - List ciphers = Arrays.asList(rfcCipherName); + List ciphers = Collections.singletonList(rfcCipherName); final SslContext sslServerContext = SslContextBuilder.forServer(CERT.certificate(), CERT.privateKey()) .sslProvider(serverSslProvider) @@ -113,9 +135,6 @@ public void testHandshake() throws Exception { .build(); try { - final ByteBuf request = Unpooled.wrappedBuffer(new byte[] {'P', 'I', 'N', 'G'}); - final ByteBuf response = Unpooled.wrappedBuffer(new byte[] {'P', 'O', 'N', 'G'}); - final Promise serverPromise = GROUP.next().newPromise(); final Promise clientPromise = GROUP.next().newPromise(); @@ -125,7 +144,7 @@ protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(sslServerContext.newHandler(ch.alloc())); - pipeline.addLast(new ChannelInboundHandlerAdapter() { + pipeline.addLast(new SimpleChannelInboundHandler() { @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { serverPromise.cancel(true); @@ -133,11 +152,10 @@ public void channelInactive(ChannelHandlerContext ctx) throws Exception { } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (serverPromise.trySuccess(msg)) { - ctx.writeAndFlush(response.slice()); + public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + if (serverPromise.trySuccess(null)) { + ctx.writeAndFlush(Unpooled.wrappedBuffer(new byte[] {'P', 'O', 'N', 'G'})); } - ctx.close(); } @@ -152,7 +170,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E }; LocalAddress address = new LocalAddress("test-" + serverSslProvider - + "-" + clientSslProvider + "-" + rfcCipherName); + + '-' + clientSslProvider + '-' + rfcCipherName); Channel server = server(address, serverHandler); try { @@ -162,7 +180,7 @@ protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(sslClientContext.newHandler(ch.alloc())); - pipeline.addLast(new ChannelInboundHandlerAdapter() { + pipeline.addLast(new SimpleChannelInboundHandler() { @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { clientPromise.cancel(true); @@ -170,8 +188,8 @@ public void channelInactive(ChannelHandlerContext ctx) throws Exception { } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - clientPromise.trySuccess(msg); + public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + clientPromise.trySuccess(null); ctx.close(); } @@ -188,7 +206,8 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) Channel client = client(server, clientHandler); try { - client.writeAndFlush(request.slice()); + client.writeAndFlush(Unpooled.wrappedBuffer(new byte[] {'P', 'I', 'N', 'G'})) + .syncUninterruptibly(); assertTrue("client timeout", clientPromise.await(5L, TimeUnit.SECONDS)); assertTrue("server timeout", serverPromise.await(5L, TimeUnit.SECONDS)); From 187b1b8a551bc8973b7544e21c0ef8bda92f40e5 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 1 Sep 2018 08:33:32 +0200 Subject: [PATCH 152/417] Correctly implement SelectedSelectionKeySet.remove(...) / contains(...) again so it works with the NIO Selector. Motivation: c1a335446daf5892eab2e134538ba0388162f18b reimplemented remove(...) and contains(...) in a way which made it not work anymore when used by the Selector. Modifications: Partly revert changes in c1a335446daf5892eab2e134538ba0388162f18b. Result: Works again as expected --- .../io/netty/channel/nio/SelectedSelectionKeySet.java | 10 ++++++++++ .../channel/nio/SelectedSelectionKeySetTest.java | 11 +++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java b/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java index 452f0f0d77c6..0c7a1cc1f581 100644 --- a/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java +++ b/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java @@ -44,6 +44,16 @@ public boolean add(SelectionKey o) { return true; } + @Override + public boolean remove(Object o) { + return false; + } + + @Override + public boolean contains(Object o) { + return false; + } + @Override public int size() { return size; diff --git a/transport/src/test/java/io/netty/channel/nio/SelectedSelectionKeySetTest.java b/transport/src/test/java/io/netty/channel/nio/SelectedSelectionKeySetTest.java index 83506406925e..88bb7b0a29a9 100644 --- a/transport/src/test/java/io/netty/channel/nio/SelectedSelectionKeySetTest.java +++ b/transport/src/test/java/io/netty/channel/nio/SelectedSelectionKeySetTest.java @@ -97,8 +97,8 @@ public void contains() { SelectedSelectionKeySet set = new SelectedSelectionKeySet(); assertTrue(set.add(mockKey)); assertTrue(set.add(mockKey2)); - assertTrue(set.contains(mockKey)); - assertTrue(set.contains(mockKey2)); + assertFalse(set.contains(mockKey)); + assertFalse(set.contains(mockKey2)); assertFalse(set.contains(mockKey3)); } @@ -106,12 +106,7 @@ public void contains() { public void remove() { SelectedSelectionKeySet set = new SelectedSelectionKeySet(); assertTrue(set.add(mockKey)); + assertFalse(set.remove(mockKey)); assertFalse(set.remove(mockKey2)); - try { - set.remove(mockKey); - fail(); - } catch (UnsupportedOperationException expected) { - // expected - } } } From c74b3f3a3b73fee125048b0f486fc9c19fb3bc14 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 1 Sep 2018 08:59:08 +0200 Subject: [PATCH 153/417] Correctly implement SelectedSelectionKeySet.Iterator remove() Motivation: We need to implement remove() by ourselves to make it work on Java7 as otherwise it will throw an AbstractMethodError. This is a followup of c1a335446daf5892eab2e134538ba0388162f18b. Modifications: Just implemented remove() Result: Works on Java7 as well. --- .../java/io/netty/channel/nio/SelectedSelectionKeySet.java | 5 +++++ transport/test.log | 0 2 files changed, 5 insertions(+) create mode 100644 transport/test.log diff --git a/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java b/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java index 0c7a1cc1f581..8be2672a5a2c 100644 --- a/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java +++ b/transport/src/main/java/io/netty/channel/nio/SelectedSelectionKeySet.java @@ -76,6 +76,11 @@ public SelectionKey next() { } return keys[idx++]; } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } }; } diff --git a/transport/test.log b/transport/test.log new file mode 100644 index 000000000000..e69de29bb2d1 From 3eec66a974f85bb154966750678348afdb694789 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 3 Sep 2018 18:07:53 +0200 Subject: [PATCH 154/417] Do not fail on runtime when an older version of Log4J2 is on the classpath. (#8240) Motivation: At the moment we will just assume the correct version of log4j2 is used when we find it on the classpath. This may lead to an AbstractMethodError at runtime. We should not use log4j2 if the version is not correct. Modifications: Check on class loading if we can use Log4J2 or not. Result: Fixes #8217. --- .../util/internal/logging/Log4J2Logger.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/common/src/main/java/io/netty/util/internal/logging/Log4J2Logger.java b/common/src/main/java/io/netty/util/internal/logging/Log4J2Logger.java index 36f28083753b..5c3593f203ae 100644 --- a/common/src/main/java/io/netty/util/internal/logging/Log4J2Logger.java +++ b/common/src/main/java/io/netty/util/internal/logging/Log4J2Logger.java @@ -21,14 +21,42 @@ import org.apache.logging.log4j.spi.ExtendedLogger; import org.apache.logging.log4j.spi.ExtendedLoggerWrapper; +import java.security.AccessController; +import java.security.PrivilegedAction; + import static io.netty.util.internal.logging.AbstractInternalLogger.EXCEPTION_MESSAGE; class Log4J2Logger extends ExtendedLoggerWrapper implements InternalLogger { private static final long serialVersionUID = 5485418394879791397L; + private static final boolean VARARGS_ONLY; + + static { + // Older Log4J2 versions have only log methods that takes the format + varargs. So we should not use + // Log4J2 if the version is too old. + // See https://github.com/netty/netty/issues/8217 + VARARGS_ONLY = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Boolean run() { + try { + Logger.class.getMethod("debug", String.class, Object.class); + return false; + } catch (NoSuchMethodException ignore) { + // Log4J2 version too old. + return true; + } catch (SecurityException ignore) { + // We could not detect the version so we will use Log4J2 if its on the classpath. + return false; + } + } + }); + } Log4J2Logger(Logger logger) { super((ExtendedLogger) logger, logger.getName(), logger.getMessageFactory()); + if (VARARGS_ONLY) { + throw new UnsupportedOperationException("Log4J2 version mismatch"); + } } @Override From c78be334433af4b0d95bfa78d041a748a8490033 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Mon, 3 Sep 2018 20:33:47 +0200 Subject: [PATCH 155/417] Added configurable ByteBuf bounds checking (#7521) Motivation: The JVM isn't always able to hoist out/reduce bounds checking (due to ref counting operations etc etc) hence making it configurable could improve performances for most CPU intensive use cases. Modifications: Each AbstractByteBuf bounds check has been tested against a new static final configuration property similar to checkAccessible ie io.netty.buffer.bytebuf.checkBounds. Result: Any user could disable ByteBuf bounds checking in order to get extra performances. --- .../java/io/netty/buffer/AbstractByteBuf.java | 115 +++++++++++------- .../microbench/buffer/ByteBufBenchmark.java | 7 +- .../buffer/HeapByteBufBenchmark.java | 5 + 3 files changed, 83 insertions(+), 44 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index 4a13d821b933..c8c40a5d2448 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -43,13 +43,22 @@ */ public abstract class AbstractByteBuf extends ByteBuf { private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractByteBuf.class); - private static final String PROP_MODE = "io.netty.buffer.bytebuf.checkAccessible"; + private static final String LEGACY_PROP_CHECK_ACCESSIBLE = "io.netty.buffer.bytebuf.checkAccessible"; + private static final String PROP_CHECK_ACCESSIBLE = "io.netty.buffer.checkAccessible"; private static final boolean checkAccessible; + private static final String PROP_CHECK_BOUNDS = "io.netty.buffer.checkBounds"; + private static final boolean checkBounds; static { - checkAccessible = SystemPropertyUtil.getBoolean(PROP_MODE, true); + if (SystemPropertyUtil.contains(PROP_CHECK_ACCESSIBLE)) { + checkAccessible = SystemPropertyUtil.getBoolean(PROP_CHECK_ACCESSIBLE, true); + } else { + checkAccessible = SystemPropertyUtil.getBoolean(LEGACY_PROP_CHECK_ACCESSIBLE, true); + } + checkBounds = SystemPropertyUtil.getBoolean(PROP_CHECK_BOUNDS, true); if (logger.isDebugEnabled()) { - logger.debug("-D{}: {}", PROP_MODE, checkAccessible); + logger.debug("-D{}: {}", PROP_CHECK_ACCESSIBLE, checkAccessible); + logger.debug("-D{}: {}", PROP_CHECK_BOUNDS, checkBounds); } } @@ -97,11 +106,18 @@ public int readerIndex() { return readerIndex; } + private static void checkIndexBounds(final int readerIndex, final int writerIndex, final int capacity) { + if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity) { + throw new IndexOutOfBoundsException(String.format( + "readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))", + readerIndex, writerIndex, capacity)); + } + } + @Override public ByteBuf readerIndex(int readerIndex) { - if (readerIndex < 0 || readerIndex > writerIndex) { - throw new IndexOutOfBoundsException(String.format( - "readerIndex: %d (expected: 0 <= readerIndex <= writerIndex(%d))", readerIndex, writerIndex)); + if (checkBounds) { + checkIndexBounds(readerIndex, writerIndex, capacity()); } this.readerIndex = readerIndex; return this; @@ -114,10 +130,8 @@ public int writerIndex() { @Override public ByteBuf writerIndex(int writerIndex) { - if (writerIndex < readerIndex || writerIndex > capacity()) { - throw new IndexOutOfBoundsException(String.format( - "writerIndex: %d (expected: readerIndex(%d) <= writerIndex <= capacity(%d))", - writerIndex, readerIndex, capacity())); + if (checkBounds) { + checkIndexBounds(readerIndex, writerIndex, capacity()); } this.writerIndex = writerIndex; return this; @@ -125,10 +139,8 @@ public ByteBuf writerIndex(int writerIndex) { @Override public ByteBuf setIndex(int readerIndex, int writerIndex) { - if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity()) { - throw new IndexOutOfBoundsException(String.format( - "readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))", - readerIndex, writerIndex, capacity())); + if (checkBounds) { + checkIndexBounds(readerIndex, writerIndex, capacity()); } setIndex0(readerIndex, writerIndex); return this; @@ -271,11 +283,12 @@ final void ensureWritable0(int minWritableBytes) { if (minWritableBytes <= writableBytes()) { return; } - - if (minWritableBytes > maxCapacity - writerIndex) { - throw new IndexOutOfBoundsException(String.format( - "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s", - writerIndex, minWritableBytes, maxCapacity, this)); + if (checkBounds) { + if (minWritableBytes > maxCapacity - writerIndex) { + throw new IndexOutOfBoundsException(String.format( + "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s", + writerIndex, minWritableBytes, maxCapacity, this)); + } } // Normalize the current capacity to the power of 2. @@ -618,15 +631,21 @@ public ByteBuf setBytes(int index, ByteBuf src) { return this; } + private static void checkReadableBounds(final ByteBuf src, final int length) { + if (length > src.readableBytes()) { + throw new IndexOutOfBoundsException(String.format( + "length(%d) exceeds src.readableBytes(%d) where src is: %s", length, src.readableBytes(), src)); + } + } + @Override public ByteBuf setBytes(int index, ByteBuf src, int length) { checkIndex(index, length); if (src == null) { throw new NullPointerException("src"); } - if (length > src.readableBytes()) { - throw new IndexOutOfBoundsException(String.format( - "length(%d) exceeds src.readableBytes(%d) where src is: %s", length, src.readableBytes(), src)); + if (checkBounds) { + checkReadableBounds(src, length); } setBytes(index, src, src.readerIndex(), length); @@ -889,9 +908,11 @@ public ByteBuf readBytes(ByteBuf dst) { @Override public ByteBuf readBytes(ByteBuf dst, int length) { - if (length > dst.writableBytes()) { - throw new IndexOutOfBoundsException(String.format( - "length(%d) exceeds dst.writableBytes(%d) where dst is: %s", length, dst.writableBytes(), dst)); + if (checkBounds) { + if (length > dst.writableBytes()) { + throw new IndexOutOfBoundsException(String.format( + "length(%d) exceeds dst.writableBytes(%d) where dst is: %s", length, dst.writableBytes(), dst)); + } } readBytes(dst, dst.writerIndex(), length); dst.writerIndex(dst.writerIndex() + length); @@ -1065,9 +1086,8 @@ public ByteBuf writeBytes(ByteBuf src) { @Override public ByteBuf writeBytes(ByteBuf src, int length) { - if (length > src.readableBytes()) { - throw new IndexOutOfBoundsException(String.format( - "length(%d) exceeds src.readableBytes(%d) where src is: %s", length, src.readableBytes(), src)); + if (checkBounds) { + checkReadableBounds(src, length); } writeBytes(src, src.readerIndex(), length); src.readerIndex(src.readerIndex() + length); @@ -1357,26 +1377,30 @@ protected final void checkIndex(int index, int fieldLength) { checkIndex0(index, fieldLength); } - final void checkIndex0(int index, int fieldLength) { - if (isOutOfBounds(index, fieldLength, capacity())) { + private static void checkRangeBounds(final int index, final int fieldLength, final int capacity) { + if (isOutOfBounds(index, fieldLength, capacity)) { throw new IndexOutOfBoundsException(String.format( - "index: %d, length: %d (expected: range(0, %d))", index, fieldLength, capacity())); + "index: %d, length: %d (expected: range(0, %d))", index, fieldLength, capacity)); + } + } + + final void checkIndex0(int index, int fieldLength) { + if (checkBounds) { + checkRangeBounds(index, fieldLength, capacity()); } } protected final void checkSrcIndex(int index, int length, int srcIndex, int srcCapacity) { checkIndex(index, length); - if (isOutOfBounds(srcIndex, length, srcCapacity)) { - throw new IndexOutOfBoundsException(String.format( - "srcIndex: %d, length: %d (expected: range(0, %d))", srcIndex, length, srcCapacity)); + if (checkBounds) { + checkRangeBounds(srcIndex, length, srcCapacity); } } protected final void checkDstIndex(int index, int length, int dstIndex, int dstCapacity) { checkIndex(index, length); - if (isOutOfBounds(dstIndex, length, dstCapacity)) { - throw new IndexOutOfBoundsException(String.format( - "dstIndex: %d, length: %d (expected: range(0, %d))", dstIndex, length, dstCapacity)); + if (checkBounds) { + checkRangeBounds(dstIndex, length, dstCapacity); } } @@ -1394,17 +1418,22 @@ protected final void checkReadableBytes(int minimumReadableBytes) { protected final void checkNewCapacity(int newCapacity) { ensureAccessible(); - if (newCapacity < 0 || newCapacity > maxCapacity()) { - throw new IllegalArgumentException("newCapacity: " + newCapacity + " (expected: 0-" + maxCapacity() + ')'); + if (checkBounds) { + if (newCapacity < 0 || newCapacity > maxCapacity()) { + throw new IllegalArgumentException("newCapacity: " + newCapacity + + " (expected: 0-" + maxCapacity() + ')'); + } } } private void checkReadableBytes0(int minimumReadableBytes) { ensureAccessible(); - if (readerIndex > writerIndex - minimumReadableBytes) { - throw new IndexOutOfBoundsException(String.format( - "readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s", - readerIndex, minimumReadableBytes, writerIndex, this)); + if (checkBounds) { + if (readerIndex > writerIndex - minimumReadableBytes) { + throw new IndexOutOfBoundsException(String.format( + "readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s", + readerIndex, minimumReadableBytes, writerIndex, this)); + } } } diff --git a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/ByteBufBenchmark.java index 70d1ceef532a..c1cfa39add1d 100644 --- a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufBenchmark.java +++ b/microbench/src/main/java/io/netty/microbench/buffer/ByteBufBenchmark.java @@ -20,6 +20,7 @@ import io.netty.buffer.Unpooled; import io.netty.microbench.util.AbstractMicrobenchmark; import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.TearDown; @@ -27,10 +28,13 @@ public class ByteBufBenchmark extends AbstractMicrobenchmark { static { - System.setProperty("io.netty.buffer.bytebuf.checkAccessible", "false"); + System.setProperty("io.netty.buffer.checkAccessible", "false"); } private static final byte BYTE = '0'; + @Param({ "true", "false" }) + public String checkBounds; + private ByteBuffer byteBuffer; private ByteBuffer directByteBuffer; private ByteBuf buffer; @@ -39,6 +43,7 @@ public class ByteBufBenchmark extends AbstractMicrobenchmark { @Setup public void setup() { + System.setProperty("io.netty.buffer.checkBounds", checkBounds); byteBuffer = ByteBuffer.allocate(8); directByteBuffer = ByteBuffer.allocateDirect(8); buffer = Unpooled.buffer(8); diff --git a/microbench/src/main/java/io/netty/microbench/buffer/HeapByteBufBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/HeapByteBufBenchmark.java index b7cf0c5fa8fe..06df3af9cb95 100644 --- a/microbench/src/main/java/io/netty/microbench/buffer/HeapByteBufBenchmark.java +++ b/microbench/src/main/java/io/netty/microbench/buffer/HeapByteBufBenchmark.java @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.microbench.util.AbstractMicrobenchmark; import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.TearDown; @@ -26,6 +27,9 @@ public class HeapByteBufBenchmark extends AbstractMicrobenchmark { + @Param({ "true", "false" }) + public String checkBounds; + private ByteBuf unsafeBuffer; private ByteBuf buffer; @@ -39,6 +43,7 @@ private static ByteBuf newBuffer(String classname) throws Exception { @Setup public void setup() throws Exception { + System.setProperty("io.netty.buffer.bytebuf.checkBounds", checkBounds); unsafeBuffer = newBuffer("io.netty.buffer.UnpooledUnsafeHeapByteBuf"); buffer = newBuffer("io.netty.buffer.UnpooledHeapByteBuf"); unsafeBuffer.writeLong(1L); From 379a56ca49195396bc78f1d96ec2cd7164255b96 Mon Sep 17 00:00:00 2001 From: Carl Mastrangelo Date: Fri, 31 Aug 2018 13:25:07 -0700 Subject: [PATCH 156/417] Add an Epoll benchmark MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: Optimizing the Epoll channel needs an objective measure of how fast it is. Modification: Add a simple, closed loop, ping-pong benchmark. Result: Benchmark can be used to measure #7816 Initial numbers: ``` Result "io.netty.microbench.channel.epoll.EpollSocketChannelBenchmark.pingPong": 22614.403 ±(99.9%) 797.263 ops/s [Average] (min, avg, max) = (21093.160, 22614.403, 24977.387), stdev = 918.130 CI (99.9%): [21817.140, 23411.666] (assumes normal distribution) Benchmark Mode Cnt Score Error Units EpollSocketChannelBenchmark.pingPong thrpt 20 22614.403 ± 797.263 ops/s ``` --- microbench/pom.xml | 12 ++ .../epoll/EpollSocketChannelBenchmark.java | 139 ++++++++++++++++++ .../channel/epoll/package-info.java | 19 +++ 3 files changed, 170 insertions(+) create mode 100644 microbench/src/main/java/io/netty/microbench/channel/epoll/EpollSocketChannelBenchmark.java create mode 100644 microbench/src/main/java/io/netty/microbench/channel/epoll/package-info.java diff --git a/microbench/pom.xml b/microbench/pom.xml index 441cd81246e5..43127cd2b1d0 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -32,6 +32,9 @@ true 1.19 + + @@ -42,6 +45,9 @@ linux + + ${jni.classifier} + @@ -78,6 +84,12 @@ netty-codec-redis ${project.version} + + ${project.groupId} + netty-transport-native-epoll + ${project.version} + ${epoll.classifier} + junit junit diff --git a/microbench/src/main/java/io/netty/microbench/channel/epoll/EpollSocketChannelBenchmark.java b/microbench/src/main/java/io/netty/microbench/channel/epoll/EpollSocketChannelBenchmark.java new file mode 100644 index 000000000000..5ecd18668d21 --- /dev/null +++ b/microbench/src/main/java/io/netty/microbench/channel/epoll/EpollSocketChannelBenchmark.java @@ -0,0 +1,139 @@ +/* + * Copyright 2018 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: + * + * http://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.microbench.channel.epoll; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPromise; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.epoll.EpollServerSocketChannel; +import io.netty.channel.epoll.EpollSocketChannel; +import io.netty.microbench.util.AbstractMicrobenchmark; +import io.netty.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.TearDown; + +public class EpollSocketChannelBenchmark extends AbstractMicrobenchmark { + + private EpollEventLoopGroup group; + private Channel serverChan; + private Channel chan; + private ByteBuf abyte; + private ScheduledFuture future; + + @Setup + public void setup() throws Exception { + group = new EpollEventLoopGroup(1); + + // add an arbitrary timeout to make the timer reschedule + future = group.schedule(new Runnable() { + @Override + public void run() { + throw new AssertionError(); + } + }, 5, TimeUnit.MINUTES); + serverChan = new ServerBootstrap() + .channel(EpollServerSocketChannel.class) + .group(group) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addLast(new ChannelDuplexHandler() { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + if (msg instanceof ByteBuf) { + ctx.writeAndFlush(msg, ctx.voidPromise()); + } else { + throw new AssertionError(); + } + } + }); + } + }) + .bind(0) + .sync() + .channel(); + chan = new Bootstrap() + .channel(EpollSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addLast(new ChannelDuplexHandler() { + + private ChannelPromise lastWritePromise; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + if (msg instanceof ByteBuf) { + + ByteBuf buf = (ByteBuf) msg; + try { + if (buf.readableBytes() == 1) { + lastWritePromise.trySuccess(); + lastWritePromise = null; + } else { + throw new AssertionError(); + } + } finally { + buf.release(); + } + } else { + throw new AssertionError(); + } + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) + throws Exception { + if (lastWritePromise != null) { + throw new IllegalStateException(); + } + lastWritePromise = promise; + super.write(ctx, msg, ctx.voidPromise()); + } + }); + } + }) + .group(group) + .connect(serverChan.localAddress()) + .sync() + .channel(); + + abyte = chan.alloc().directBuffer(1); + abyte.writeByte('a'); + } + + @TearDown + public void tearDown() throws Exception { + chan.close().sync(); + serverChan.close().sync(); + future.cancel(true); + group.shutdownGracefully(0, 0, TimeUnit.SECONDS).sync(); + abyte.release(); + } + + @Benchmark + public Object pingPong() throws Exception { + return chan.pipeline().writeAndFlush(abyte.retainedSlice()).sync(); + } +} diff --git a/microbench/src/main/java/io/netty/microbench/channel/epoll/package-info.java b/microbench/src/main/java/io/netty/microbench/channel/epoll/package-info.java new file mode 100644 index 000000000000..08304d05d00c --- /dev/null +++ b/microbench/src/main/java/io/netty/microbench/channel/epoll/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2018 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: + * + * http://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. + */ +/** + * Benchmarks for {@link io.netty.microbench.channel.epoll}. + */ +package io.netty.microbench.channel.epoll; From dc1b511fcf6d5f902c7ca475cbaa7fdd026c62f1 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 4 Sep 2018 19:13:56 +0200 Subject: [PATCH 157/417] Correctly reset offset when fail lazy because of too long frame. (#8257) Motivation: We need to reset the offset to 0 when we fail lazy because of a too long frame. Modifications: - Reset offset - Add testcase Result: Fixes https://github.com/netty/netty/issues/8256. --- .../handler/codec/LineBasedFrameDecoder.java | 2 ++ .../codec/LineBasedFrameDecoderTest.java | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java index 305981b7622d..58f2e85f04cd 100644 --- a/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java @@ -137,6 +137,8 @@ protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Except } else { discardedBytes += buffer.readableBytes(); buffer.readerIndex(buffer.writerIndex()); + // We skip everything in the buffer, we need to set the offset to 0 again. + offset = 0; } return null; } diff --git a/codec/src/test/java/io/netty/handler/codec/LineBasedFrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/LineBasedFrameDecoderTest.java index b68c38bbbeab..9edd178fa251 100644 --- a/codec/src/test/java/io/netty/handler/codec/LineBasedFrameDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/LineBasedFrameDecoderTest.java @@ -185,4 +185,27 @@ public void testEmptyLine() throws Exception { buf.release(); buf2.release(); } + + @Test + public void testNotFailFast() throws Exception { + EmbeddedChannel ch = new EmbeddedChannel(new LineBasedFrameDecoder(2, false, false)); + assertFalse(ch.writeInbound(wrappedBuffer(new byte[] { 0, 1, 2 }))); + assertFalse(ch.writeInbound(wrappedBuffer(new byte[]{ 3, 4 }))); + try { + ch.writeInbound(wrappedBuffer(new byte[] { '\n' })); + fail(); + } catch (TooLongFrameException expected) { + // Expected once we received a full frame. + } + assertFalse(ch.writeInbound(wrappedBuffer(new byte[] { '5' }))); + assertTrue(ch.writeInbound(wrappedBuffer(new byte[] { '\n' }))); + + ByteBuf expected = wrappedBuffer(new byte[] { '5', '\n' }); + ByteBuf buffer = ch.readInbound(); + assertEquals(expected, buffer); + expected.release(); + buffer.release(); + + assertFalse(ch.finish()); + } } From ade60c11e16db5f83070c419bcf8fc701cdd15f9 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 5 Sep 2018 07:22:16 +0200 Subject: [PATCH 158/417] PlatformDependent0 should be able to better detect if unaligned access is supported on java9 and later. (#8255) Motivation: In Java8 and earlier we used reflection to detect if unaligned access is supported. This fails in Java9 and later as we would need to change the accessible level of the method. Lucky enough we can use Unsafe directly to read the content of the static field here. Modifications: Add special handling for detecting if unaligned access is supported on Java9 and later which does not fail due jigsaw. Result: Better and more correct detection on Java9 and later. --- .../util/internal/PlatformDependent0.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java index 53e9ed7bdcf2..cf3c4712733e 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java @@ -262,13 +262,32 @@ public Object run() { DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor; ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField); BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); - boolean unaligned; + final boolean unaligned; Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { try { Class bitsClass = Class.forName("java.nio.Bits", false, getSystemClassLoader()); + int version = javaVersion(); + if (version >= 9) { + // Java9/10 use all lowercase and later versions all uppercase. + String fieldName = version >= 11 ? "UNALIGNED" : "unaligned"; + // On Java9 and later we try to directly access the field as we can do this without + // adjust the accessible levels. + try { + Field unalignedField = bitsClass.getDeclaredField(fieldName); + if (unalignedField.getType() == boolean.class) { + long offset = UNSAFE.staticFieldOffset(unalignedField); + Object object = UNSAFE.staticFieldBase(unalignedField); + return UNSAFE.getBoolean(object, offset); + } + // There is something unexpected stored in the field, + // let us fall-back and try to use a reflective method call as last resort. + } catch (NoSuchFieldException ignore) { + // We did not find the field we expected, move on. + } + } Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned"); Throwable cause = ReflectionUtil.trySetAccessible(unalignedMethod, true); if (cause != null) { From 3c2dbdb5db30bc0ce691f29aadff55c3f18dd60b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 5 Sep 2018 07:23:03 +0200 Subject: [PATCH 159/417] NioEventLoop should also use our special SelectionKeySet on Java9 and later. (#8260) Motivation: In Java8 and earlier we used reflection to replace the used key set if not otherwise told. This does not work on Java9 and later without special flags as its not possible to call setAccessible(true) on the Field anymore. Modifications: - Use Unsafe to instrument the Selector with out special set when sun.misc.Unsafe is present and we are using Java9+. Result: NIO transport produce less GC on Java9 and later as well. --- .../util/internal/PlatformDependent.java | 9 ++++++++ .../util/internal/PlatformDependent0.java | 4 ++++ .../io/netty/channel/nio/NioEventLoop.java | 22 ++++++++++++++++--- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index 64d740aaf34e..fa38e8921d91 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -29,6 +29,7 @@ import org.jctools.util.UnsafeAccess; import java.io.File; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -567,6 +568,14 @@ public static void putLong(byte[] data, int index, long value) { PlatformDependent0.putLong(data, index, value); } + public static void putObject(Object o, long offset, Object x) { + PlatformDependent0.putObject(o, offset, x); + } + + public static long objectFieldOffset(Field field) { + return PlatformDependent0.objectFieldOffset(field); + } + public static void copyMemory(long srcAddr, long dstAddr, long length) { PlatformDependent0.copyMemory(srcAddr, dstAddr, length); } diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java index cf3c4712733e..df45d1614a0e 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java @@ -568,6 +568,10 @@ static void putLong(byte[] data, int index, long value) { UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value); } + static void putObject(Object o, long offset, Object x) { + UNSAFE.putObject(o, offset, x); + } + static void copyMemory(long srcAddr, long dstAddr, long length) { // Manual safe-point polling is only needed prior Java9: // See https://bugs.openjdk.java.net/browse/JDK-8149596 diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index e36e24a27978..a95d514c1964 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -43,7 +43,6 @@ import java.util.Iterator; import java.util.Queue; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -188,8 +187,8 @@ public Object run() { }); if (!(maybeSelectorImplClass instanceof Class) || - // ensure the current selector implementation is what we can instrument. - !((Class) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) { + // ensure the current selector implementation is what we can instrument. + !((Class) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) { if (maybeSelectorImplClass instanceof Throwable) { Throwable t = (Throwable) maybeSelectorImplClass; logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t); @@ -207,6 +206,23 @@ public Object run() { Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys"); Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys"); + if (PlatformDependent.javaVersion() >= 9 && PlatformDependent.hasUnsafe()) { + // Let us try to use sun.misc.Unsafe to replace the SelectionKeySet. + // This allows us to also do this in Java9+ without any extra flags. + long selectedKeysFieldOffset = PlatformDependent.objectFieldOffset(selectedKeysField); + long publicSelectedKeysFieldOffset = + PlatformDependent.objectFieldOffset(publicSelectedKeysField); + + if (selectedKeysFieldOffset != -1 && publicSelectedKeysFieldOffset != -1) { + PlatformDependent.putObject( + unwrappedSelector, selectedKeysFieldOffset, selectedKeySet); + PlatformDependent.putObject( + unwrappedSelector, publicSelectedKeysFieldOffset, selectedKeySet); + return null; + } + // We could not retrieve the offset, lets try reflection as last-resort. + } + Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField, true); if (cause != null) { return cause; From 8635d88d4de056b82f86af901ff6cc95b37e422b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 5 Sep 2018 18:28:35 +0200 Subject: [PATCH 160/417] Allow to generate a jmh uber jar to run benchmarks easily from cmdline with different arguments. (#8264) Motivation: It is sometimes useful to be able to run benchmarks easily from the commandline and passs different arguments / options here. We should support this. Modifications: Add the benchmark-jar profile which allows to generate such an "uber-jar" that can be used directly to run benchmarks as documented at http://openjdk.java.net/projects/code-tools/jmh/. Result: More flexible way to run benchmarks. --- microbench/pom.xml | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/microbench/pom.xml b/microbench/pom.xml index 43127cd2b1d0..3146e5665b6a 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -61,6 +61,48 @@ + + benchmark-jar + + + + org.apache.maven.plugins + maven-shade-plugin + 2.2 + + + package + + shade + + + microbenchmarks + + + org.openjdk.jmh.Main + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + From 02d559e6a4fa0f77b5d406996238d8aa485d0b61 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 5 Sep 2018 19:05:02 +0200 Subject: [PATCH 161/417] Remove flags when running benchmarks. (#8262) Motivation: Some of the flags we used are not supported anymore on more recent JDK versions. We should just remove all of them and only keep what we really need. This may also reflect better what people use in production. Modifications: Remove some flags when running the benchmarks. Result: Benchmarks also run with JDK11. --- .../io/netty/microbench/util/AbstractMicrobenchmarkBase.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmarkBase.java b/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmarkBase.java index 058658789f6f..a623cc89ea96 100644 --- a/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmarkBase.java +++ b/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmarkBase.java @@ -43,8 +43,7 @@ public abstract class AbstractMicrobenchmarkBase { protected static final int DEFAULT_WARMUP_ITERATIONS = 10; protected static final int DEFAULT_MEASURE_ITERATIONS = 10; protected static final String[] BASE_JVM_ARGS = { - "-server", "-dsa", "-da", "-ea:io.netty...", "-XX:+AggressiveOpts", "-XX:+UseBiasedLocking", - "-XX:+UseFastAccessorMethods", "-XX:+OptimizeStringConcat", + "-server", "-dsa", "-da", "-ea:io.netty...", "-XX:+HeapDumpOnOutOfMemoryError", "-Dio.netty.leakDetection.level=disabled"}; static { From 5ff6b579403eacb6288f50cd0d5fb5641c1aa276 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 5 Sep 2018 20:33:40 +0200 Subject: [PATCH 162/417] =?UTF-8?q?PemPrivateKey.toPem(...)=20should=20thr?= =?UTF-8?q?ow=20IllegalArgumentException=20when=20P=E2=80=A6=20(#8253)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * PemPrivateKey.toPem(...) should throw IllegalArgumentException when PrivateKey which does not support encoding is used. Motivation: At the moment when a PrivateKey is used that does not support encoding we throw a NPE when trying to convert the key. We should better throw an IllegalArgumentException with the details about what key we tried to encode. Modifications: - Check if PrivateKey.getEncoded() returns null and if so throw an IllegalArgumentException - Add unit test. Result: Better handling of non-supported PrivateKey implementations. --- .../io/netty/handler/ssl/PemPrivateKey.java | 7 +++++- .../io/netty/handler/ssl/PemEncodedTest.java | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/PemPrivateKey.java b/handler/src/main/java/io/netty/handler/ssl/PemPrivateKey.java index e7bfd12392a8..46145a04b1ec 100644 --- a/handler/src/main/java/io/netty/handler/ssl/PemPrivateKey.java +++ b/handler/src/main/java/io/netty/handler/ssl/PemPrivateKey.java @@ -60,7 +60,12 @@ static PemEncoded toPEM(ByteBufAllocator allocator, boolean useDirect, PrivateKe return ((PemEncoded) key).retain(); } - ByteBuf encoded = Unpooled.wrappedBuffer(key.getEncoded()); + byte[] bytes = key.getEncoded(); + if (bytes == null) { + throw new IllegalArgumentException(key.getClass().getName() + " does not support encoding"); + } + + ByteBuf encoded = Unpooled.wrappedBuffer(bytes); try { ByteBuf base64 = SslUtils.toBase64(allocator, encoded); try { diff --git a/handler/src/test/java/io/netty/handler/ssl/PemEncodedTest.java b/handler/src/test/java/io/netty/handler/ssl/PemEncodedTest.java index 793f77228785..b2531eb8f6dc 100644 --- a/handler/src/test/java/io/netty/handler/ssl/PemEncodedTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/PemEncodedTest.java @@ -24,7 +24,10 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.security.PrivateKey; +import io.netty.buffer.Unpooled; +import io.netty.buffer.UnpooledByteBufAllocator; import org.junit.Test; import io.netty.handler.ssl.util.SelfSignedCertificate; @@ -69,6 +72,26 @@ private static void testPemEncoded(SslProvider provider) throws Exception { } } + @Test(expected = IllegalArgumentException.class) + public void testEncodedReturnsNull() throws Exception { + PemPrivateKey.toPEM(UnpooledByteBufAllocator.DEFAULT, true, new PrivateKey() { + @Override + public String getAlgorithm() { + return null; + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return null; + } + }); + } + private static void assertRelease(PemEncoded encoded) { assertTrue(encoded.release()); } From 052c2fbefeea9f9da7d8299488e689e60aec25a9 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 6 Sep 2018 22:31:52 +0200 Subject: [PATCH 163/417] Update to jmh 1.2.1 (#8270) Motivation: We should use the latest jmh version which also supports -prof dtraceasm on MacOS. Modifications: Update to latest jmh version. Result: Better benchmark / profiling support on MacOS. --- microbench/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microbench/pom.xml b/microbench/pom.xml index 3146e5665b6a..ba1c5f6f3bb2 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -31,7 +31,7 @@ true - 1.19 + 1.21 From afe0767e9c150369cd6ea150b5cc1a9021fd5c3c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 7 Sep 2018 07:34:22 +0200 Subject: [PATCH 164/417] Log the correct line-number when using SLF4j with netty if possible. (#8258) * Log the correct line-number when using SLF4j with netty if possible. Motivation: At the moment we do not log the correct line number in many cases as it will log the line number of the logger wrapper itself. Slf4j does have an extra interface that can be used to filter out these nad make it more usable with logging wrappers. Modifications: Detect if the returned logger implements LocationAwareLogger and if so make use of its extra methods to be able to log the correct origin of the log request. Result: Better logging when using slf4j. --- .../logging/LocationAwareSlf4JLogger.java | 248 ++++++++++++++++++ .../util/internal/logging/Slf4JLogger.java | 2 +- .../internal/logging/Slf4JLoggerFactory.java | 10 +- .../logging/Slf4JLoggerFactoryTest.java | 27 +- 4 files changed, 283 insertions(+), 4 deletions(-) create mode 100644 common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java diff --git a/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java b/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java new file mode 100644 index 000000000000..13073b02b418 --- /dev/null +++ b/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java @@ -0,0 +1,248 @@ +/* + * Copyright 2017 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: + * + * http://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.internal.logging; + +import org.slf4j.spi.LocationAwareLogger; + +import static org.slf4j.spi.LocationAwareLogger.*; + +/** + * SLF4J logger which is location aware and so will log the correct origin of the + * logging event by filter out the wrapper itself. + */ +final class LocationAwareSlf4JLogger extends AbstractInternalLogger { + + // IMPORTANT: All our log methods first check if the log level is enabled before call the wrapped + // LocationAwareLogger.log(...) method. This is done to reduce GC creation that is caused by varargs. + + private static final String FQCN = LocationAwareSlf4JLogger.class.getName(); + private static final long serialVersionUID = -8292030083201538180L; + + private final transient LocationAwareLogger logger; + + LocationAwareSlf4JLogger(LocationAwareLogger logger) { + super(logger.getName()); + this.logger = logger; + } + + private void log(final int level, final String message, final Object... params) { + logger.log(null, FQCN, level, message, params, null); + } + + private void log(final int level, final String message, Throwable throwable, final Object... params) { + logger.log(null, FQCN, level, message, params, throwable); + } + + @Override + public boolean isTraceEnabled() { + return logger.isTraceEnabled(); + } + + @Override + public void trace(String msg) { + if (isTraceEnabled()) { + log(TRACE_INT, msg, null); + } + } + + @Override + public void trace(String format, Object arg) { + if (isTraceEnabled()) { + log(TRACE_INT, format, arg); + } + } + + @Override + public void trace(String format, Object argA, Object argB) { + if (isTraceEnabled()) { + log(TRACE_INT, format, argA, argB); + } + } + + @Override + public void trace(String format, Object... argArray) { + if (isTraceEnabled()) { + log(TRACE_INT, format, argArray); + } + } + + @Override + public void trace(String msg, Throwable t) { + if (isTraceEnabled()) { + log(TRACE_INT, msg, t); + } + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public void debug(String msg) { + if (isDebugEnabled()) { + log(DEBUG_INT, msg); + } + } + + @Override + public void debug(String format, Object arg) { + if (isDebugEnabled()) { + log(DEBUG_INT, format, arg); + } + } + + @Override + public void debug(String format, Object argA, Object argB) { + if (isDebugEnabled()) { + log(DEBUG_INT, format, argA, argB); + } + } + + @Override + public void debug(String format, Object... argArray) { + if (isDebugEnabled()) { + log(DEBUG_INT, format, argArray); + } + } + + @Override + public void debug(String msg, Throwable t) { + if (isDebugEnabled()) { + log(DEBUG_INT, msg, t); + } + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + @Override + public void info(String msg) { + if (isInfoEnabled()) { + log(INFO_INT, msg); + } + } + + @Override + public void info(String format, Object arg) { + if (isInfoEnabled()) { + log(INFO_INT, format, arg); + } + } + + @Override + public void info(String format, Object argA, Object argB) { + if (isInfoEnabled()) { + log(INFO_INT, format, argA, argB); + } + } + + @Override + public void info(String format, Object... argArray) { + if (isInfoEnabled()) { + log(INFO_INT, format, argArray); + } + } + + @Override + public void info(String msg, Throwable t) { + if (isInfoEnabled()) { + log(INFO_INT, msg, t); + } + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(); + } + + @Override + public void warn(String msg) { + if (isWarnEnabled()) { + log(WARN_INT, msg); + } + } + + @Override + public void warn(String format, Object arg) { + if (isWarnEnabled()) { + log(WARN_INT, format, arg); + } + } + + @Override + public void warn(String format, Object... argArray) { + if (isWarnEnabled()) { + log(WARN_INT, format, argArray); + } + } + + @Override + public void warn(String format, Object argA, Object argB) { + if (isWarnEnabled()) { + log(WARN_INT, format, argA, argB); + } + } + + @Override + public void warn(String msg, Throwable t) { + if (isWarnEnabled()) { + log(WARN_INT, msg, t); + } + } + + @Override + public boolean isErrorEnabled() { + return logger.isErrorEnabled(); + } + + @Override + public void error(String msg) { + if (isErrorEnabled()) { + log(ERROR_INT, msg); + } + } + + @Override + public void error(String format, Object arg) { + if (isErrorEnabled()) { + log(ERROR_INT, format, arg); + } + } + + @Override + public void error(String format, Object argA, Object argB) { + if (isErrorEnabled()) { + log(ERROR_INT, format, argA, argB); + } + } + + @Override + public void error(String format, Object... argArray) { + if (isErrorEnabled()) { + log(ERROR_INT, format, argArray); + } + } + + @Override + public void error(String msg, Throwable t) { + if (isErrorEnabled()) { + log(ERROR_INT, msg, t); + } + } +} diff --git a/common/src/main/java/io/netty/util/internal/logging/Slf4JLogger.java b/common/src/main/java/io/netty/util/internal/logging/Slf4JLogger.java index e78727ca0f58..d628456a79d7 100644 --- a/common/src/main/java/io/netty/util/internal/logging/Slf4JLogger.java +++ b/common/src/main/java/io/netty/util/internal/logging/Slf4JLogger.java @@ -20,7 +20,7 @@ /** * SLF4J logger. */ -class Slf4JLogger extends AbstractInternalLogger { +final class Slf4JLogger extends AbstractInternalLogger { private static final long serialVersionUID = 108038972685130825L; diff --git a/common/src/main/java/io/netty/util/internal/logging/Slf4JLoggerFactory.java b/common/src/main/java/io/netty/util/internal/logging/Slf4JLoggerFactory.java index 42df0a1b76ea..0d97a5ea9e2f 100644 --- a/common/src/main/java/io/netty/util/internal/logging/Slf4JLoggerFactory.java +++ b/common/src/main/java/io/netty/util/internal/logging/Slf4JLoggerFactory.java @@ -16,8 +16,10 @@ package io.netty.util.internal.logging; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.NOPLoggerFactory; +import org.slf4j.spi.LocationAwareLogger; /** * Logger factory which creates a SLF4J @@ -44,6 +46,12 @@ public Slf4JLoggerFactory() { @Override public InternalLogger newInstance(String name) { - return new Slf4JLogger(LoggerFactory.getLogger(name)); + return wrapLogger(LoggerFactory.getLogger(name)); + } + + // package-private for testing. + static InternalLogger wrapLogger(Logger logger) { + return logger instanceof LocationAwareLogger ? + new LocationAwareSlf4JLogger((LocationAwareLogger) logger) : new Slf4JLogger(logger); } } diff --git a/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java b/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java index 9ace34d5511b..6b3cd8501589 100644 --- a/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java +++ b/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java @@ -16,15 +16,38 @@ package io.netty.util.internal.logging; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.spi.LocationAwareLogger; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class Slf4JLoggerFactoryTest { @Test public void testCreation() { InternalLogger logger = Slf4JLoggerFactory.INSTANCE.newInstance("foo"); - assertTrue(logger instanceof Slf4JLogger); + assertTrue(logger instanceof Slf4JLogger || logger instanceof LocationAwareSlf4JLogger); assertEquals("foo", logger.name()); } + + @Test + public void testCreationLogger() { + Logger logger = mock(Logger.class); + when(logger.getName()).thenReturn("testlogger"); + InternalLogger internalLogger = Slf4JLoggerFactory.wrapLogger(logger); + assertTrue(internalLogger instanceof Slf4JLogger); + assertEquals("testlogger", internalLogger.name()); + } + + @Test + public void testCreationLocationAwareLogger() { + Logger logger = mock(LocationAwareLogger.class); + when(logger.getName()).thenReturn("testlogger"); + InternalLogger internalLogger = Slf4JLoggerFactory.wrapLogger(logger); + assertTrue(internalLogger instanceof LocationAwareSlf4JLogger); + assertEquals("testlogger", internalLogger.name()); + } } From e542a2cf26774885a87014f8b47ea22aa8157da0 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 7 Sep 2018 07:47:02 +0200 Subject: [PATCH 165/417] Use a non-volatile read for ensureAccessible() whenever possible to reduce overhead and allow better inlining. (#8266) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motiviation: At the moment whenever ensureAccessible() is called in our ByteBuf implementations (which is basically on each operation) we will do a volatile read. That per-se is not such a bad thing but the problem here is that it will also reduce the the optimizations that the compiler / jit can do. For example as these are volatile it can not eliminate multiple loads of it when inline the methods of ByteBuf which happens quite frequently because most of them a quite small and very hot. That is especially true for all the methods that act on primitives. It gets even worse as people often call a lot of these after each other in the same method or even use method chaining here. The idea of the change is basically just ue a non-volatile read for the ensureAccessible() check as its a best-effort implementation to detect acting on already released buffers anyway as even with a volatile read it could happen that the user will release it in another thread before we actual access the buffer after the reference check. Modifications: - Try to do a non-volatile read using sun.misc.Unsafe if we can use it. - Add a benchmark Result: Big performance win when multiple ByteBuf methods are called from a method. With the change: UnsafeByteBufBenchmark.setGetLongUnsafeByteBuf thrpt 20 281395842,128 ± 5050792,296 ops/s Before the change: UnsafeByteBufBenchmark.setGetLongUnsafeByteBuf thrpt 20 217419832,801 ± 5080579,030 ops/s --- .../java/io/netty/buffer/AbstractByteBuf.java | 10 ++- .../AbstractReferenceCountedByteBuf.java | 29 ++++++++- .../netty/buffer/WrappedCompositeByteBuf.java | 5 ++ .../buffer/UnsafeByteBufBenchmark.java | 64 +++++++++++++++++++ 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 microbench/src/main/java/io/netty/microbench/buffer/UnsafeByteBufBenchmark.java diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index c8c40a5d2448..1fe1dbe27209 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -1442,11 +1442,19 @@ private void checkReadableBytes0(int minimumReadableBytes) { * if the buffer was released before. */ protected final void ensureAccessible() { - if (checkAccessible && refCnt() == 0) { + if (checkAccessible && internalRefCnt() == 0) { throw new IllegalReferenceCountException(0); } } + /** + * Returns the reference count that is used internally by {@link #ensureAccessible()} to try to guard + * against using the buffer after it was released (best-effort). + */ + int internalRefCnt() { + return refCnt(); + } + final void setIndex0(int readerIndex, int writerIndex) { this.readerIndex = readerIndex; this.writerIndex = writerIndex; diff --git a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java index d624d855f4da..84de93fab654 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java @@ -17,6 +17,7 @@ package io.netty.buffer; import io.netty.util.IllegalReferenceCountException; +import io.netty.util.internal.PlatformDependent; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; @@ -26,17 +27,40 @@ * Abstract base class for {@link ByteBuf} implementations that count references. */ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf { - + private static final long REFCNT_FIELD_OFFSET; private static final AtomicIntegerFieldUpdater refCntUpdater = AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt"); private volatile int refCnt; + static { + long refCntFieldOffset = -1; + try { + if (PlatformDependent.hasUnsafe()) { + refCntFieldOffset = PlatformDependent.objectFieldOffset( + AbstractReferenceCountedByteBuf.class.getDeclaredField("refCnt")); + } + } catch (Throwable ignore) { + refCntFieldOffset = -1; + } + + REFCNT_FIELD_OFFSET = refCntFieldOffset; + } + protected AbstractReferenceCountedByteBuf(int maxCapacity) { super(maxCapacity); refCntUpdater.set(this, 1); } + @Override + int internalRefCnt() { + // Try to do non-volatile read for performance as the ensureAccessible() is racy anyway and only provide + // a best-effort guard. + // + // TODO: Once we compile against later versions of Java we can replace the Unsafe usage here by varhandles. + return REFCNT_FIELD_OFFSET != -1 ? PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET) : refCnt(); + } + @Override public int refCnt() { return refCnt; @@ -94,7 +118,8 @@ private boolean release0(int decrement) { if (oldRef == decrement) { deallocate(); return true; - } else if (oldRef < decrement || oldRef - decrement > oldRef) { + } + if (oldRef < decrement || oldRef - decrement > oldRef) { // Ensure we don't over-release, and avoid underflow. refCntUpdater.getAndAdd(this, decrement); throw new IllegalReferenceCountException(oldRef, -decrement); diff --git a/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java index 8f5161620f98..b124eb2d2b96 100644 --- a/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java @@ -423,6 +423,11 @@ public final int refCnt() { return wrapped.refCnt(); } + @Override + int internalRefCnt() { + return wrapped.internalRefCnt(); + } + @Override public ByteBuf duplicate() { return wrapped.duplicate(); diff --git a/microbench/src/main/java/io/netty/microbench/buffer/UnsafeByteBufBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/UnsafeByteBufBenchmark.java new file mode 100644 index 000000000000..435feb61d8f3 --- /dev/null +++ b/microbench/src/main/java/io/netty/microbench/buffer/UnsafeByteBufBenchmark.java @@ -0,0 +1,64 @@ +/* +* Copyright 2018 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: +* +* http://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.microbench.buffer; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.buffer.UnpooledUnsafeDirectByteBuf; +import io.netty.microbench.util.AbstractMicrobenchmark; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.TearDown; + +import java.nio.ByteBuffer; + + +public class UnsafeByteBufBenchmark extends AbstractMicrobenchmark { + + private ByteBuf unsafeBuffer; + private ByteBuffer byteBuffer; + + @Setup + public void setup() { + unsafeBuffer = new UnpooledUnsafeDirectByteBuf(UnpooledByteBufAllocator.DEFAULT, 64, 64); + byteBuffer = ByteBuffer.allocateDirect(64); + } + + @TearDown + public void tearDown() { + unsafeBuffer.release(); + } + + @Benchmark + public long setGetLongUnsafeByteBuf() { + return unsafeBuffer.setLong(0, 1).getLong(0); + } + + @Benchmark + public long setGetLongByteBuffer() { + return byteBuffer.putLong(0, 1).getLong(0); + } + + @Benchmark + public ByteBuf setLongUnsafeByteBuf() { + return unsafeBuffer.setLong(0, 1); + } + + @Benchmark + public ByteBuffer setLongByteBuffer() { + return byteBuffer.putLong(0, 1); + } +} From c14efd952d7901924116eaff1431a88feccee320 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 7 Sep 2018 19:04:19 +0200 Subject: [PATCH 166/417] Directly init refCnt to 1 (#8274) Motivation: We should just directly init the refCnt to 1 and not use the AtomicIntegerFieldUpdater. Modifications: Just assing directly to 1. Result: Cleaner code and possible a bit faster as the JVM / JIT may be able to optimize the first store easily. --- .../java/io/netty/buffer/AbstractReferenceCountedByteBuf.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java index 84de93fab654..8582c302a73e 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java @@ -31,7 +31,7 @@ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf { private static final AtomicIntegerFieldUpdater refCntUpdater = AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt"); - private volatile int refCnt; + private volatile int refCnt = 1; static { long refCntFieldOffset = -1; @@ -49,7 +49,6 @@ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf { protected AbstractReferenceCountedByteBuf(int maxCapacity) { super(maxCapacity); - refCntUpdater.set(this, 1); } @Override From 2a1596a4e9f6f9e5dad94ee2e83c4eb4f9dce0c6 Mon Sep 17 00:00:00 2001 From: Matteo Merli Date: Fri, 7 Sep 2018 11:50:51 -0700 Subject: [PATCH 167/417] Allow to configure socket option SO_BUSY_POLL (#8268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: When using Epoll based transport, allow applications to configure SO_BUSY_POLL socket option: SO_BUSY_POLL (since Linux 3.11) Sets the approximate time in microseconds to busy poll on a blocking receive when there is no data. Increasing this value requires CAP_NET_ADMIN. The default for this option is con‐ trolled by the /proc/sys/net/core/busy_read file. The value in the /proc/sys/net/core/busy_poll file determines how long select(2) and poll(2) will busy poll when they oper‐ ate on sockets with SO_BUSY_POLL set and no events to report are found. In both cases, busy polling will only be done when the socket last received data from a network device that supports this option. While busy polling may improve latency of some applications, care must be taken when using it since this will increase both CPU utilization and power usage. Modification: Added SO_BUSY_POLL socket option Result: Able to configure SO_BUSY_POLL from Netty --- .../src/main/c/netty_epoll_linuxsocket.c | 19 ++++++++++++ .../channel/epoll/EpollChannelOption.java | 1 + .../epoll/EpollSocketChannelConfig.java | 30 ++++++++++++++++++- .../io/netty/channel/epoll/LinuxSocket.java | 10 +++++++ 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c b/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c index d6e3ead94fee..05d889f175c0 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c +++ b/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c @@ -50,6 +50,11 @@ #define TCP_NOTSENT_LOWAT 25 #endif +// SO_BUSY_POLL is defined in linux 3.11. We define this here so older kernels can compile. +#ifndef SO_BUSY_POLL +#define SO_BUSY_POLL 46 +#endif + static jclass peerCredentialsClass = NULL; static jmethodID peerCredentialsMethodId = NULL; @@ -111,6 +116,10 @@ static void netty_epoll_linuxsocket_setIpRecvOrigDestAddr(JNIEnv* env, jclass cl netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_RECVORIGDSTADDR, &optval, sizeof(optval)); } +static void netty_epoll_linuxsocket_setSoBusyPoll(JNIEnv* env, jclass clazz, jint fd, jint optval) { + netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_BUSY_POLL, &optval, sizeof(optval)); +} + static void netty_epoll_linuxsocket_setTcpMd5Sig(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jbyteArray key) { struct sockaddr_storage addr; socklen_t addrSize; @@ -256,6 +265,14 @@ static jint netty_epoll_linuxsocket_isTcpCork(JNIEnv* env, jclass clazz, jint fd return optval; } +static jint netty_epoll_linuxsocket_getSoBusyPoll(JNIEnv* env, jclass clazz, jint fd) { + int optval; + if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_BUSY_POLL, &optval, sizeof(optval)) == -1) { + return -1; + } + return optval; +} + static jint netty_epoll_linuxsocket_getTcpDeferAccept(JNIEnv* env, jclass clazz, jint fd) { int optval; if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &optval, sizeof(optval)) == -1) { @@ -341,10 +358,12 @@ static jlong netty_epoll_linuxsocket_sendFile(JNIEnv* env, jclass clazz, jint fd // JNI Method Registration Table Begin static const JNINativeMethod fixed_method_table[] = { { "setTcpCork", "(II)V", (void *) netty_epoll_linuxsocket_setTcpCork }, + { "setSoBusyPoll", "(II)V", (void *) netty_epoll_linuxsocket_setSoBusyPoll }, { "setTcpQuickAck", "(II)V", (void *) netty_epoll_linuxsocket_setTcpQuickAck }, { "setTcpDeferAccept", "(II)V", (void *) netty_epoll_linuxsocket_setTcpDeferAccept }, { "setTcpNotSentLowAt", "(II)V", (void *) netty_epoll_linuxsocket_setTcpNotSentLowAt }, { "isTcpCork", "(I)I", (void *) netty_epoll_linuxsocket_isTcpCork }, + { "getSoBusyPoll", "(I)I", (void *) netty_epoll_linuxsocket_getSoBusyPoll }, { "getTcpDeferAccept", "(I)I", (void *) netty_epoll_linuxsocket_getTcpDeferAccept }, { "getTcpNotSentLowAt", "(I)I", (void *) netty_epoll_linuxsocket_getTcpNotSentLowAt }, { "isTcpQuickAck", "(I)I", (void *) netty_epoll_linuxsocket_isTcpQuickAck }, diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java index d03e3e723678..1f5127c5dc3e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java @@ -38,6 +38,7 @@ public final class EpollChannelOption extends UnixChannelOption { public static final ChannelOption TCP_DEFER_ACCEPT = ChannelOption.valueOf(EpollChannelOption.class, "TCP_DEFER_ACCEPT"); public static final ChannelOption TCP_QUICKACK = valueOf(EpollChannelOption.class, "TCP_QUICKACK"); + public static final ChannelOption SO_BUSY_POLL = valueOf(EpollChannelOption.class, "SO_BUSY_POLL"); public static final ChannelOption EPOLL_MODE = ChannelOption.valueOf(EpollChannelOption.class, "EPOLL_MODE"); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java index 67468910b28e..d61bf19e27e9 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java @@ -62,7 +62,7 @@ public Map, Object> getOptions() { ALLOW_HALF_CLOSURE, EpollChannelOption.TCP_CORK, EpollChannelOption.TCP_NOTSENT_LOWAT, EpollChannelOption.TCP_KEEPCNT, EpollChannelOption.TCP_KEEPIDLE, EpollChannelOption.TCP_KEEPINTVL, EpollChannelOption.TCP_MD5SIG, EpollChannelOption.TCP_QUICKACK, EpollChannelOption.IP_TRANSPARENT, - EpollChannelOption.TCP_FASTOPEN_CONNECT); + EpollChannelOption.TCP_FASTOPEN_CONNECT, EpollChannelOption.SO_BUSY_POLL); } @SuppressWarnings("unchecked") @@ -119,6 +119,9 @@ public T getOption(ChannelOption option) { if (option == EpollChannelOption.TCP_FASTOPEN_CONNECT) { return (T) Boolean.valueOf(isTcpFastOpenConnect()); } + if (option == EpollChannelOption.SO_BUSY_POLL) { + return (T) Integer.valueOf(getSoBusyPoll()); + } return super.getOption(option); } @@ -164,6 +167,8 @@ public boolean setOption(ChannelOption option, T value) { setTcpQuickAck((Boolean) value); } else if (option == EpollChannelOption.TCP_FASTOPEN_CONNECT) { setTcpFastOpenConnect((Boolean) value); + } else if (option == EpollChannelOption.SO_BUSY_POLL) { + setSoBusyPoll((Integer) value); } else { return super.setOption(option, value); } @@ -245,6 +250,17 @@ public boolean isTcpCork() { } } + /** + * Get the {@code SO_BUSY_POLL} option on the socket. See {@code man 7 tcp} for more details. + */ + public int getSoBusyPoll() { + try { + return channel.socket.getSoBusyPoll(); + } catch (IOException e) { + throw new ChannelException(e); + } + } + /** * Get the {@code TCP_NOTSENT_LOWAT} option on the socket. See {@code man 7 tcp} for more details. * @return value is a uint32_t @@ -380,6 +396,18 @@ public EpollSocketChannelConfig setTcpCork(boolean tcpCork) { } } + /** + * Set the {@code SO_BUSY_POLL} option on the socket. See {@code man 7 tcp} for more details. + */ + public EpollSocketChannelConfig setSoBusyPoll(int loopMicros) { + try { + channel.socket.setSoBusyPoll(loopMicros); + return this; + } catch (IOException e) { + throw new ChannelException(e); + } + } + /** * Set the {@code TCP_NOTSENT_LOWAT} option on the socket. See {@code man 7 tcp} for more details. * @param tcpNotSentLowAt is a uint32_t diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java index f0a5304a74cf..d3578b4dae7e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java @@ -56,6 +56,10 @@ void setTcpCork(boolean tcpCork) throws IOException { setTcpCork(intValue(), tcpCork ? 1 : 0); } + void setSoBusyPoll(int loopMicros) throws IOException { + setSoBusyPoll(intValue(), loopMicros); + } + void setTcpNotSentLowAt(long tcpNotSentLowAt) throws IOException { if (tcpNotSentLowAt < 0 || tcpNotSentLowAt > MAX_UINT32_T) { throw new IllegalArgumentException("tcpNotSentLowAt must be a uint32_t"); @@ -116,6 +120,10 @@ boolean isTcpCork() throws IOException { return isTcpCork(intValue()) != 0; } + int getSoBusyPoll() throws IOException { + return getSoBusyPoll(intValue()); + } + int getTcpDeferAccept() throws IOException { return getTcpDeferAccept(intValue()); } @@ -190,6 +198,7 @@ private static native long sendFile(int socketFd, DefaultFileRegion src, long ba private static native int getTcpDeferAccept(int fd) throws IOException; private static native int isTcpQuickAck(int fd) throws IOException; private static native int isTcpCork(int fd) throws IOException; + private static native int getSoBusyPoll(int fd) throws IOException; private static native int getTcpNotSentLowAt(int fd) throws IOException; private static native int getTcpKeepIdle(int fd) throws IOException; private static native int getTcpKeepIntvl(int fd) throws IOException; @@ -205,6 +214,7 @@ private static native long sendFile(int socketFd, DefaultFileRegion src, long ba private static native void setTcpDeferAccept(int fd, int deferAccept) throws IOException; private static native void setTcpQuickAck(int fd, int quickAck) throws IOException; private static native void setTcpCork(int fd, int tcpCork) throws IOException; + private static native void setSoBusyPoll(int fd, int loopMicros) throws IOException; private static native void setTcpNotSentLowAt(int fd, int tcpNotSentLowAt) throws IOException; private static native void setTcpFastOpen(int fd, int tcpFastopenBacklog) throws IOException; private static native void setTcpFastOpenConnect(int fd, int tcpFastOpenConnect) throws IOException; From 1dff107de1645e39a54796c4f297f014358ddfcd Mon Sep 17 00:00:00 2001 From: Carl Mastrangelo Date: Tue, 11 Sep 2018 04:38:38 -0700 Subject: [PATCH 168/417] Don't re-arm timerfd each epoll_wait (#7816) Motivation: The Epoll transport checks to see if there are any scheduled tasks before entering epoll_wait, and resets the timerfd just before. This causes an extra syscall to timerfd_settime before doing any actual work. When scheduled tasks aren't added frequently, or tasks are added with later deadlines, this is unnecessary. Modification: Check the *deadline* of the peeked task in EpollEventLoop, rather than the *delay*. If it hasn't changed since last time, don't re-arm the timer Result: About 2us faster on gRPC RTT 50pct latency benchmarks. Before (2 runs for 5 minutes, 1 minute of warmup): ``` 50.0%ile Latency (in nanos): 64267 90.0%ile Latency (in nanos): 72851 95.0%ile Latency (in nanos): 78903 99.0%ile Latency (in nanos): 92327 99.9%ile Latency (in nanos): 119691 100.0%ile Latency (in nanos): 13347327 QPS: 14933 50.0%ile Latency (in nanos): 63907 90.0%ile Latency (in nanos): 73055 95.0%ile Latency (in nanos): 79443 99.0%ile Latency (in nanos): 93739 99.9%ile Latency (in nanos): 123583 100.0%ile Latency (in nanos): 14028287 QPS: 14936 ``` After: ``` 50.0%ile Latency (in nanos): 62123 90.0%ile Latency (in nanos): 70795 95.0%ile Latency (in nanos): 76895 99.0%ile Latency (in nanos): 90887 99.9%ile Latency (in nanos): 117819 100.0%ile Latency (in nanos): 14126591 QPS: 15387 50.0%ile Latency (in nanos): 61021 90.0%ile Latency (in nanos): 70311 95.0%ile Latency (in nanos): 76687 99.0%ile Latency (in nanos): 90887 99.9%ile Latency (in nanos): 119527 100.0%ile Latency (in nanos): 6351615 QPS: 15571 ``` --- .../concurrent/SingleThreadEventExecutor.java | 13 +++++++++++++ .../src/main/c/netty_epoll_native.c | 18 +++++++++++------- .../netty/channel/epoll/EpollEventLoop.java | 19 +++++++++++++++---- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java index be5814f5955e..67959016152d 100644 --- a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java +++ b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java @@ -443,6 +443,19 @@ protected long delayNanos(long currentTimeNanos) { return scheduledTask.delayNanos(currentTimeNanos); } + /** + * Returns the absolute point in time (relative to {@link #nanoTime()}) at which the the next + * closest scheduled task should run. + */ + @UnstableApi + protected long deadlineNanos() { + ScheduledFutureTask scheduledTask = peekScheduledTask(); + if (scheduledTask == null) { + return nanoTime() + SCHEDULE_PURGE_INTERVAL; + } + return scheduledTask.deadlineNanos(); + } + /** * Updates the internal timestamp that tells when a submitted task was executed most recently. * {@link #runAllTasks()} and {@link #runAllTasks(long)} updates this timestamp automatically, and thus there's diff --git a/transport-native-epoll/src/main/c/netty_epoll_native.c b/transport-native-epoll/src/main/c/netty_epoll_native.c index e6a38b4f233a..19a83724197a 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_native.c +++ b/transport-native-epoll/src/main/c/netty_epoll_native.c @@ -182,13 +182,17 @@ static jint netty_epoll_native_epollWait0(JNIEnv* env, jclass clazz, jint efd, j } } while((err = errno) == EINTR); } else { - struct itimerspec ts; - memset(&ts.it_interval, 0, sizeof(struct timespec)); - ts.it_value.tv_sec = tvSec; - ts.it_value.tv_nsec = tvNsec; - if (timerfd_settime(timerFd, 0, &ts, NULL) < 0) { - netty_unix_errors_throwChannelExceptionErrorNo(env, "timerfd_settime() failed: ", errno); - return -1; + // only reschedule the timer if there is a newer event. + // -1 is a special value used by EpollEventLoop. + if (tvSec != ((jint) -1) && tvNsec != ((jint) -1)) { + struct itimerspec ts; + memset(&ts.it_interval, 0, sizeof(struct timespec)); + ts.it_value.tv_sec = tvSec; + ts.it_value.tv_nsec = tvNsec; + if (timerfd_settime(timerFd, 0, &ts, NULL) < 0) { + netty_unix_errors_throwChannelExceptionErrorNo(env, "timerfd_settime() failed: ", errno); + return -1; + } } do { result = epoll_wait(efd, ev, len, -1); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index c90a2ca1069e..462b0b0a336a 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -55,6 +55,8 @@ final class EpollEventLoop extends SingleThreadEventLoop { Epoll.ensureAvailability(); } + // Pick a number that no task could have previously used. + private long prevDeadlineNanos = nanoTime() - 1; private final FileDescriptor epollFd; private final FileDescriptor eventFd; private final FileDescriptor timerFd; @@ -236,10 +238,19 @@ private int epollWait(boolean oldWakeup) throws IOException { return epollWaitNow(); } - long totalDelay = delayNanos(System.nanoTime()); - int delaySeconds = (int) min(totalDelay / 1000000000L, Integer.MAX_VALUE); - return Native.epollWait(epollFd, events, timerFd, delaySeconds, - (int) min(MAX_SCHEDULED_TIMERFD_NS, totalDelay - delaySeconds * 1000000000L)); + int delaySeconds; + int delayNanos; + long curDeadlineNanos = deadlineNanos(); + if (curDeadlineNanos == prevDeadlineNanos) { + delaySeconds = -1; + delayNanos = -1; + } else { + long totalDelay = delayNanos(System.nanoTime()); + prevDeadlineNanos = curDeadlineNanos; + delaySeconds = (int) min(totalDelay / 1000000000L, Integer.MAX_VALUE); + delayNanos = (int) min(totalDelay - delaySeconds * 1000000000L, Integer.MAX_VALUE); + } + return Native.epollWait(epollFd, events, timerFd, delaySeconds, delayNanos); } private int epollWaitNow() throws IOException { From 9eb124bb629163f4b22ad223f611ed8b4eaa74e8 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 11 Sep 2018 20:34:37 +0200 Subject: [PATCH 169/417] Don't cause ClassCastException if registration fails during constructing DnsNameResolver. (#8280) Motivation: We should not try to cast the Channel to a DatagramChannel as this will cause a ClassCastException. Modifications: - Do not cast - rethrow from constructor if we detect the registration failed. - Add unit test. Result: Propagate correct exception. --- .../io/netty/resolver/dns/DnsNameResolver.java | 15 +++++++++++++-- .../netty/resolver/dns/DnsNameResolverTest.java | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java index c90a749d8dd4..63df0e8421c9 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java @@ -152,7 +152,7 @@ public class DnsNameResolver extends InetNameResolver { private static final DatagramDnsQueryEncoder ENCODER = new DatagramDnsQueryEncoder(); final Future channelFuture; - final DatagramChannel ch; + final Channel ch; // Comparator that ensures we will try first to use the nameservers that use our preferred address type. private final Comparator nameServerComparator; @@ -356,7 +356,18 @@ protected void initChannel(DatagramChannel ch) throws Exception { }); channelFuture = responseHandler.channelActivePromise; - ch = (DatagramChannel) b.register().channel(); + ChannelFuture future = b.register(); + Throwable cause = future.cause(); + if (cause != null) { + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } + if (cause instanceof Error) { + throw (Error) cause; + } + throw new IllegalStateException("Unable to create / register Channel", cause); + } + ch = future.channel(); ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize)); ch.closeFuture().addListener(new ChannelFutureListener() { diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java index 684e205312fd..a3d39b4b978f 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java @@ -2270,4 +2270,20 @@ public void testResolveACachedWithDotSearchDomain() throws Exception { server.stop(); } } + + @Test + public void testChannelFactoryException() { + final IllegalStateException exception = new IllegalStateException(); + try { + newResolver().channelFactory(new ChannelFactory() { + @Override + public DatagramChannel newChannel() { + throw exception; + } + }).build(); + fail(); + } catch (Exception e) { + assertSame(exception, e); + } + } } From 6ed7c6c75d458047adc37470697f215e9d7436ea Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 14 Sep 2018 14:33:11 +0200 Subject: [PATCH 170/417] Return an ExtendSSLSession whenever possible to allow more strict checking when using OpenSSL (#8281) Motivation: When an ExtendedSSLSession is used its possible to do more strict checking of the keys during handshake. We should do this whenever possible. Modification: - Return an ExtendedSSLSession when using client-mode and Java7+ - Add unit test - Simplify unit tests Result: More consistent behaviour. --- .../handler/ssl/ExtendedOpenSslSession.java | 179 ++++++++++++++++ .../io/netty/handler/ssl/Java8SslUtils.java | 9 +- .../io/netty/handler/ssl/OpenSslSession.java | 36 ++++ .../ssl/ReferenceCountedOpenSslEngine.java | 37 ++-- .../handler/ssl/SniClientJava8TestUtil.java | 200 ++++++++++++++++++ .../io/netty/handler/ssl/SniClientTest.java | 131 ++++++------ pom.xml | 1 + 7 files changed, 511 insertions(+), 82 deletions(-) create mode 100644 handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java create mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslSession.java diff --git a/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java b/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java new file mode 100644 index 000000000000..041e99f2e235 --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java @@ -0,0 +1,179 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.util.internal.EmptyArrays; + +import javax.net.ssl.ExtendedSSLSession; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSessionContext; +import javax.security.cert.X509Certificate; +import java.security.Principal; +import java.security.cert.Certificate; +import java.util.List; + +/** + * Delegates all operations to a wrapped {@link OpenSslSession} except the methods defined by {@link ExtendedSSLSession} + * itself. + */ +abstract class ExtendedOpenSslSession extends ExtendedSSLSession implements OpenSslSession { + + // TODO: use OpenSSL API to actually fetch the real data but for now just do what Conscrypt does: + // https://github.com/google/conscrypt/blob/1.2.0/common/ + // src/main/java/org/conscrypt/Java7ExtendedSSLSession.java#L32 + private static final String[] LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS = { + "SHA512withRSA", "SHA512withECDSA", "SHA384withRSA", "SHA384withECDSA", "SHA256withRSA", + "SHA256withECDSA", "SHA224withRSA", "SHA224withECDSA", "SHA1withRSA", "SHA1withECDSA", + }; + + private final OpenSslSession wrapped; + + ExtendedOpenSslSession(OpenSslSession wrapped) { + assert !(wrapped instanceof ExtendedSSLSession); + this.wrapped = wrapped; + } + + // Use rawtypes an unchecked override to be able to also work on java7. + @SuppressWarnings({ "unchecked", "rawtypes" }) + public abstract List getRequestedServerNames(); + + @Override + public void handshakeFinished() throws SSLException { + wrapped.handshakeFinished(); + } + + @Override + public void tryExpandApplicationBufferSize(int packetLengthDataOnly) { + wrapped.tryExpandApplicationBufferSize(packetLengthDataOnly); + } + + @Override + public String[] getLocalSupportedSignatureAlgorithms() { + return LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS.clone(); + } + + @Override + public String[] getPeerSupportedSignatureAlgorithms() { + // Always return empty for now. + return EmptyArrays.EMPTY_STRINGS; + } + + @Override + public byte[] getId() { + return wrapped.getId(); + } + + @Override + public SSLSessionContext getSessionContext() { + return wrapped.getSessionContext(); + } + + @Override + public long getCreationTime() { + return wrapped.getCreationTime(); + } + + @Override + public long getLastAccessedTime() { + return wrapped.getLastAccessedTime(); + } + + @Override + public void invalidate() { + wrapped.invalidate(); + } + + @Override + public boolean isValid() { + return wrapped.isValid(); + } + + @Override + public void putValue(String s, Object o) { + wrapped.putValue(s, o); + } + + @Override + public Object getValue(String s) { + return wrapped.getValue(s); + } + + @Override + public void removeValue(String s) { + wrapped.removeValue(s); + } + + @Override + public String[] getValueNames() { + return wrapped.getValueNames(); + } + + @Override + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + return wrapped.getPeerCertificates(); + } + + @Override + public Certificate[] getLocalCertificates() { + return wrapped.getLocalCertificates(); + } + + @Override + public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { + return wrapped.getPeerCertificateChain(); + } + + @Override + public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { + return wrapped.getPeerPrincipal(); + } + + @Override + public Principal getLocalPrincipal() { + return wrapped.getLocalPrincipal(); + } + + @Override + public String getCipherSuite() { + return wrapped.getCipherSuite(); + } + + @Override + public String getProtocol() { + return wrapped.getProtocol(); + } + + @Override + public String getPeerHost() { + return wrapped.getPeerHost(); + } + + @Override + public int getPeerPort() { + return wrapped.getPeerPort(); + } + + @Override + public int getPacketBufferSize() { + return wrapped.getPacketBufferSize(); + } + + @Override + public int getApplicationBufferSize() { + return wrapped.getApplicationBufferSize(); + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java b/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java index b40c121748d1..648c0f2e65c6 100644 --- a/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java +++ b/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java @@ -48,11 +48,18 @@ static List getSniHostNames(SSLParameters sslParameters) { } static void setSniHostNames(SSLParameters sslParameters, List names) { + sslParameters.setServerNames(getSniHostNames(names)); + } + + static List getSniHostNames(List names) { + if (names == null || names.isEmpty()) { + return Collections.emptyList(); + } List sniServerNames = new ArrayList(names.size()); for (String name: names) { sniServerNames.add(new SNIHostName(name)); } - sslParameters.setServerNames(sniServerNames); + return sniServerNames; } static boolean getUseCipherSuitesOrder(SSLParameters sslParameters) { diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslSession.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslSession.java new file mode 100644 index 000000000000..f9fde2662e98 --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslSession.java @@ -0,0 +1,36 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; + +interface OpenSslSession extends SSLSession { + + /** + * Finish the handshake and so init everything in the {@link OpenSslSession} that should be accessible by + * the user. + */ + void handshakeFinished() throws SSLException; + + /** + * Expand (or increase) the value returned by {@link #getApplicationBufferSize()} if necessary. + *

+ * This is only called in a synchronized block, so no need to use atomic operations. + * @param packetLengthDataOnly The packet size which exceeds the current {@link #getApplicationBufferSize()}. + */ + void tryExpandApplicationBufferSize(int packetLengthDataOnly); +} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 597fc5079c47..1d608eeeb7fb 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -244,8 +244,17 @@ protected void deallocate() { OpenSsl.ensureAvailability(); this.alloc = checkNotNull(alloc, "alloc"); apn = (OpenSslApplicationProtocolNegotiator) context.applicationProtocolNegotiator(); - session = new OpenSslSession(context.sessionContext()); clientMode = context.isClient(); + if (PlatformDependent.javaVersion() >= 7 && context.isClient()) { + session = new ExtendedOpenSslSession(new DefaultOpenSslSession(context.sessionContext())) { + @Override + public List getRequestedServerNames() { + return Java8SslUtils.getSniHostNames(sniHostNames); + } + }; + } else { + session = new DefaultOpenSslSession(context.sessionContext()); + } engineMap = context.engineMap; localCerts = context.keyCertChain; keyMaterialManager = context.keyMaterialManager(); @@ -1839,7 +1848,7 @@ private static long bufferAddress(ByteBuffer b) { return Buffer.address(b); } - private final class OpenSslSession implements SSLSession { + private final class DefaultOpenSslSession implements OpenSslSession { private final OpenSslSessionContext sessionContext; // These are guarded by synchronized(OpenSslEngine.this) as handshakeFinished() may be triggered by any @@ -1855,10 +1864,14 @@ private final class OpenSslSession implements SSLSession { // lazy init for memory reasons private Map values; - OpenSslSession(OpenSslSessionContext sessionContext) { + DefaultOpenSslSession(OpenSslSessionContext sessionContext) { this.sessionContext = sessionContext; } + private SSLSessionBindingEvent newSSLSessionBindingEvent(String name) { + return new SSLSessionBindingEvent(session, name); + } + @Override public byte[] getId() { synchronized (ReferenceCountedOpenSslEngine.this) { @@ -1925,7 +1938,8 @@ public void putValue(String name, Object value) { } Object old = values.put(name, value); if (value instanceof SSLSessionBindingListener) { - ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name)); + // Use newSSLSessionBindingEvent so we alway use the wrapper if needed. + ((SSLSessionBindingListener) value).valueBound(newSSLSessionBindingEvent(name)); } notifyUnbound(old, name); } @@ -1965,7 +1979,8 @@ public String[] getValueNames() { private void notifyUnbound(Object value, String name) { if (value instanceof SSLSessionBindingListener) { - ((SSLSessionBindingListener) value).valueUnbound(new SSLSessionBindingEvent(this, name)); + // Use newSSLSessionBindingEvent so we alway use the wrapper if needed. + ((SSLSessionBindingListener) value).valueUnbound(newSSLSessionBindingEvent(name)); } } @@ -1973,7 +1988,8 @@ private void notifyUnbound(Object value, String name) { * Finish the handshake and so init everything in the {@link OpenSslSession} that should be accessible by * the user. */ - void handshakeFinished() throws SSLException { + @Override + public void handshakeFinished() throws SSLException { synchronized (ReferenceCountedOpenSslEngine.this) { if (!isDestroyed()) { id = SSL.getSessionId(ssl); @@ -2191,13 +2207,8 @@ public int getApplicationBufferSize() { return applicationBufferSize; } - /** - * Expand (or increase) the value returned by {@link #getApplicationBufferSize()} if necessary. - *

- * This is only called in a synchronized block, so no need to use atomic operations. - * @param packetLengthDataOnly The packet size which exceeds the current {@link #getApplicationBufferSize()}. - */ - void tryExpandApplicationBufferSize(int packetLengthDataOnly) { + @Override + public void tryExpandApplicationBufferSize(int packetLengthDataOnly) { if (packetLengthDataOnly > MAX_PLAINTEXT_LENGTH && applicationBufferSize != MAX_RECORD_SIZE) { applicationBufferSize = MAX_RECORD_SIZE; } diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java index 7ba413c7696f..c6d05d05c71c 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java @@ -29,14 +29,42 @@ import io.netty.channel.local.LocalServerChannel; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; +import io.netty.handler.ssl.util.SimpleTrustManagerFactory; import io.netty.util.concurrent.Promise; +import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.ThrowableUtil; +import org.junit.Assert; +import javax.net.ssl.ExtendedSSLSession; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.KeyManagerFactorySpi; +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.SNIHostName; import javax.net.ssl.SNIMatcher; import javax.net.ssl.SNIServerName; +import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.X509ExtendedTrustManager; +import java.io.IOException; +import java.net.Socket; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; /** * In extra class to be able to run tests with java7 without trying to load classes that not exists in java7. @@ -125,4 +153,176 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc group.shutdownGracefully(); } } + + static void assertSSLSession(SSLSession session, String name) { + assertSSLSession(session, new SNIHostName(name)); + } + + private static void assertSSLSession(SSLSession session, SNIServerName name) { + Assert.assertNotNull(session); + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extendedSSLSession = (ExtendedSSLSession) session; + List names = extendedSSLSession.getRequestedServerNames(); + Assert.assertEquals(1, names.size()); + Assert.assertEquals(name, names.get(0)); + Assert.assertTrue(extendedSSLSession.getLocalSupportedSignatureAlgorithms().length > 0); + } + } + + static TrustManagerFactory newSniX509TrustmanagerFactory(String name) { + return new SniX509TrustmanagerFactory(new SNIHostName(name)); + } + + private static final class SniX509TrustmanagerFactory extends SimpleTrustManagerFactory { + + private final SNIServerName name; + + SniX509TrustmanagerFactory(SNIServerName name) { + this.name = name; + } + + @Override + protected void engineInit(KeyStore keyStore) throws Exception { + // NOOP + } + + @Override + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception { + // NOOP + } + + @Override + protected TrustManager[] engineGetTrustManagers() { + return new TrustManager[] { new X509ExtendedTrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) + throws CertificateException { + Assert.fail(); + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) + throws CertificateException { + Assert.fail(); + } + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) + throws CertificateException { + Assert.fail(); + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) + throws CertificateException { + assertSSLSession(sslEngine.getHandshakeSession(), name); + } + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) + throws CertificateException { + Assert.fail(); + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) + throws CertificateException { + Assert.fail(); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return EmptyArrays.EMPTY_X509_CERTIFICATES; + } + } }; + } + } + + static KeyManagerFactory newSniX509KeyManagerFactory(SelfSignedCertificate cert, String hostname) + throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, + IOException, CertificateException { + return new SniX509KeyManagerFactory( + new SNIHostName(hostname), SslContext.buildKeyManagerFactory( + new X509Certificate[] { cert.cert() }, cert.key(), null, null)); + } + + private static final class SniX509KeyManagerFactory extends KeyManagerFactory { + + SniX509KeyManagerFactory(final SNIServerName name, final KeyManagerFactory factory) { + super(new KeyManagerFactorySpi() { + @Override + protected void engineInit(KeyStore keyStore, char[] chars) + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { + factory.init(keyStore, chars); + } + + @Override + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) + throws InvalidAlgorithmParameterException { + factory.init(managerFactoryParameters); + } + + @Override + protected KeyManager[] engineGetKeyManagers() { + List managers = new ArrayList(); + for (final KeyManager km: factory.getKeyManagers()) { + if (km instanceof X509ExtendedKeyManager) { + managers.add(new X509ExtendedKeyManager() { + @Override + public String[] getClientAliases(String s, Principal[] principals) { + return ((X509ExtendedKeyManager) km).getClientAliases(s, principals); + } + + @Override + public String chooseClientAlias(String[] strings, Principal[] principals, + Socket socket) { + return ((X509ExtendedKeyManager) km).chooseClientAlias(strings, principals, socket); + } + + @Override + public String[] getServerAliases(String s, Principal[] principals) { + return ((X509ExtendedKeyManager) km).getServerAliases(s, principals); + } + + @Override + public String chooseServerAlias(String s, Principal[] principals, Socket socket) { + return ((X509ExtendedKeyManager) km).chooseServerAlias(s, principals, socket); + } + + @Override + public X509Certificate[] getCertificateChain(String s) { + return ((X509ExtendedKeyManager) km).getCertificateChain(s); + } + + @Override + public PrivateKey getPrivateKey(String s) { + return ((X509ExtendedKeyManager) km).getPrivateKey(s); + } + + @Override + public String chooseEngineClientAlias(String[] strings, Principal[] principals, + SSLEngine sslEngine) { + return ((X509ExtendedKeyManager) km) + .chooseEngineClientAlias(strings, principals, sslEngine); + } + + @Override + public String chooseEngineServerAlias(String s, Principal[] principals, + SSLEngine sslEngine) { + + SSLSession session = sslEngine.getHandshakeSession(); + assertSSLSession(session, name); + return ((X509ExtendedKeyManager) km) + .chooseEngineServerAlias(s, principals, sslEngine); + } + }); + } else { + managers.add(km); + } + } + return managers.toArray(new KeyManager[0]); + } + }, factory.getProvider(), factory.getAlgorithm()); + } + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java index ca1c9b8bf319..3c7cf9ea038f 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java @@ -33,97 +33,79 @@ import org.junit.Assert; import org.junit.Assume; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLException; - +import javax.net.ssl.TrustManagerFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +@RunWith(Parameterized.class) public class SniClientTest { - @Test(timeout = 30000) - public void testSniClientJdkSslServerJdkSsl() throws Exception { - testSniClient(SslProvider.JDK, SslProvider.JDK); - } - - @Test(timeout = 30000) - public void testSniClientOpenSslServerOpenSsl() throws Exception { - Assume.assumeTrue(OpenSsl.isAvailable()); - testSniClient(SslProvider.OPENSSL, SslProvider.OPENSSL); - } - - @Test(timeout = 30000) - public void testSniClientJdkSslServerOpenSsl() throws Exception { - Assume.assumeTrue(OpenSsl.isAvailable()); - testSniClient(SslProvider.JDK, SslProvider.OPENSSL); - } - - @Test(timeout = 30000) - public void testSniClientOpenSslServerJdkSsl() throws Exception { - Assume.assumeTrue(OpenSsl.isAvailable()); - testSniClient(SslProvider.OPENSSL, SslProvider.JDK); - } - - @Test(timeout = 30000) - public void testSniSNIMatcherMatchesClientJdkSslServerJdkSsl() throws Exception { - Assume.assumeTrue(PlatformDependent.javaVersion() >= 8); - SniClientJava8TestUtil.testSniClient(SslProvider.JDK, SslProvider.JDK, true); - } + @Parameters(name = "{index}: serverSslProvider = {0}, clientSslProvider = {1}") + public static Collection parameters() { + List providers = new ArrayList(Arrays.asList(SslProvider.values())); + if (!OpenSsl.isAvailable()) { + providers.remove(SslProvider.OPENSSL); + providers.remove(SslProvider.OPENSSL_REFCNT); + } - @Test(timeout = 30000, expected = SSLException.class) - public void testSniSNIMatcherDoesNotMatchClientJdkSslServerJdkSsl() throws Exception { - Assume.assumeTrue(PlatformDependent.javaVersion() >= 8); - SniClientJava8TestUtil.testSniClient(SslProvider.JDK, SslProvider.JDK, false); + List params = new ArrayList(); + for (SslProvider sp: providers) { + for (SslProvider cp: providers) { + params.add(new Object[] { sp, cp }); + } + } + return params; } - @Test(timeout = 30000) - public void testSniSNIMatcherMatchesClientOpenSslServerOpenSsl() throws Exception { - Assume.assumeTrue(PlatformDependent.javaVersion() >= 8); - Assume.assumeTrue(OpenSsl.isAvailable()); - SniClientJava8TestUtil.testSniClient(SslProvider.OPENSSL, SslProvider.OPENSSL, true); - } + private final SslProvider serverProvider; + private final SslProvider clientProvider; - @Test(timeout = 30000, expected = SSLException.class) - public void testSniSNIMatcherDoesNotMatchClientOpenSslServerOpenSsl() throws Exception { - Assume.assumeTrue(PlatformDependent.javaVersion() >= 8); - Assume.assumeTrue(OpenSsl.isAvailable()); - SniClientJava8TestUtil.testSniClient(SslProvider.OPENSSL, SslProvider.OPENSSL, false); + public SniClientTest(SslProvider serverProvider, SslProvider clientProvider) { + this.serverProvider = serverProvider; + this.clientProvider = clientProvider; } @Test(timeout = 30000) - public void testSniSNIMatcherMatchesClientJdkSslServerOpenSsl() throws Exception { - Assume.assumeTrue(PlatformDependent.javaVersion() >= 8); - Assume.assumeTrue(OpenSsl.isAvailable()); - SniClientJava8TestUtil.testSniClient(SslProvider.JDK, SslProvider.OPENSSL, true); - } - - @Test(timeout = 30000, expected = SSLException.class) - public void testSniSNIMatcherDoesNotMatchClientJdkSslServerOpenSsl() throws Exception { - Assume.assumeTrue(PlatformDependent.javaVersion() >= 8); - Assume.assumeTrue(OpenSsl.isAvailable()); - SniClientJava8TestUtil.testSniClient(SslProvider.JDK, SslProvider.OPENSSL, false); + public void testSniClient() throws Exception { + testSniClient(serverProvider, clientProvider); } @Test(timeout = 30000) - public void testSniSNIMatcherMatchesClientOpenSslServerJdkSsl() throws Exception { + public void testSniSNIMatcherMatchesClient() throws Exception { Assume.assumeTrue(PlatformDependent.javaVersion() >= 8); - Assume.assumeTrue(OpenSsl.isAvailable()); - SniClientJava8TestUtil.testSniClient(SslProvider.OPENSSL, SslProvider.JDK, true); + SniClientJava8TestUtil.testSniClient(serverProvider, clientProvider, true); } @Test(timeout = 30000, expected = SSLException.class) - public void testSniSNIMatcherDoesNotMatchClientOpenSslServerJdkSsl() throws Exception { + public void testSniSNIMatcherDoesNotMatchClient() throws Exception { Assume.assumeTrue(PlatformDependent.javaVersion() >= 8); - Assume.assumeTrue(OpenSsl.isAvailable()); - SniClientJava8TestUtil.testSniClient(SslProvider.OPENSSL, SslProvider.JDK, false); + SniClientJava8TestUtil.testSniClient(serverProvider, clientProvider, false); } private static void testSniClient(SslProvider sslClientProvider, SslProvider sslServerProvider) throws Exception { - final String sniHost = "sni.netty.io"; + String sniHostName = "sni.netty.io"; LocalAddress address = new LocalAddress("test"); EventLoopGroup group = new DefaultEventLoopGroup(1); Channel sc = null; Channel cc = null; try { SelfSignedCertificate cert = new SelfSignedCertificate(); - final SslContext sslServerContext = SslContextBuilder.forServer(cert.key(), cert.cert()) + + KeyManagerFactory kmf = PlatformDependent.javaVersion() >= 8 ? + SniClientJava8TestUtil.newSniX509KeyManagerFactory(cert, sniHostName) : + SslContext.buildKeyManagerFactory( + new X509Certificate[] { cert.cert() }, cert.key(), null, null); + + final SslContext sslServerContext = SslContextBuilder.forServer(kmf) .sslProvider(sslServerProvider).build(); final Promise promise = group.next().newPromise(); @@ -141,13 +123,26 @@ public SslContext map(String input) { } }).bind(address).syncUninterruptibly().channel(); - SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider).build(); + TrustManagerFactory tmf = PlatformDependent.javaVersion() >= 8 ? + SniClientJava8TestUtil.newSniX509TrustmanagerFactory(sniHostName) : + InsecureTrustManagerFactory.INSTANCE; + SslContext sslContext = SslContextBuilder.forClient().trustManager(tmf) + .sslProvider(sslClientProvider).build(); Bootstrap cb = new Bootstrap(); - cc = cb.group(group).channel(LocalChannel.class).handler(new SslHandler( - sslContext.newEngine(ByteBufAllocator.DEFAULT, sniHost, -1))) + + SslHandler handler = new SslHandler( + sslContext.newEngine(ByteBufAllocator.DEFAULT, sniHostName, -1)); + cc = cb.group(group).channel(LocalChannel.class).handler(handler) .connect(address).syncUninterruptibly().channel(); - Assert.assertEquals(sniHost, promise.syncUninterruptibly().getNow()); + Assert.assertEquals(sniHostName, promise.syncUninterruptibly().getNow()); + + // After we are done with handshaking getHandshakeSession() should return null. + handler.handshakeFuture().syncUninterruptibly(); + Assert.assertNull(handler.engine().getHandshakeSession()); + + if (PlatformDependent.javaVersion() >= 8) { + SniClientJava8TestUtil.assertSSLSession(handler.engine().getSession(), sniHostName); + } } finally { if (cc != null) { cc.close().syncUninterruptibly(); diff --git a/pom.xml b/pom.xml index b30b9545ae3b..702828a86da0 100644 --- a/pom.xml +++ b/pom.xml @@ -721,6 +721,7 @@ javax.net.ssl.SSLEngine + javax.net.ssl.ExtendedSSLSession javax.net.ssl.X509ExtendedTrustManager javax.net.ssl.SSLParameters javax.net.ssl.SNIServerName From 2b1514ec5a34c03883731910f69416c7873fd864 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 14 Sep 2018 19:01:55 +0200 Subject: [PATCH 171/417] Only use KeyManagerFactory in SniClientTest when supported by OpenSSL version. (#8289) Motivation: 6ed7c6c75d458047adc37470697f215e9d7436ea added a test which blindly assumed we can use a KeyManagerFactory all the time. This is only true if have OpenSSL 1.0.2 or later, which may not be the case. Modifications: Only use KeyManagerFactory in test if the OpenSSL version does support it. Result: More robust tests. --- .../io/netty/handler/ssl/SniClientTest.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java index 3c7cf9ea038f..238594bbf44c 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java @@ -91,7 +91,7 @@ public void testSniSNIMatcherDoesNotMatchClient() throws Exception { SniClientJava8TestUtil.testSniClient(serverProvider, clientProvider, false); } - private static void testSniClient(SslProvider sslClientProvider, SslProvider sslServerProvider) throws Exception { + private static void testSniClient(SslProvider sslServerProvider, SslProvider sslClientProvider) throws Exception { String sniHostName = "sni.netty.io"; LocalAddress address = new LocalAddress("test"); EventLoopGroup group = new DefaultEventLoopGroup(1); @@ -100,13 +100,23 @@ private static void testSniClient(SslProvider sslClientProvider, SslProvider ssl try { SelfSignedCertificate cert = new SelfSignedCertificate(); - KeyManagerFactory kmf = PlatformDependent.javaVersion() >= 8 ? - SniClientJava8TestUtil.newSniX509KeyManagerFactory(cert, sniHostName) : - SslContext.buildKeyManagerFactory( - new X509Certificate[] { cert.cert() }, cert.key(), null, null); - - final SslContext sslServerContext = SslContextBuilder.forServer(kmf) - .sslProvider(sslServerProvider).build(); + final SslContext sslServerContext; + if ((sslServerProvider == SslProvider.OPENSSL || sslServerProvider == SslProvider.OPENSSL_REFCNT) + && !OpenSsl.useKeyManagerFactory()) { + sslServerContext = SslContextBuilder.forServer(cert.certificate(), cert.privateKey()) + .sslProvider(sslServerProvider) + .build(); + } else { + // The used OpenSSL version does support a KeyManagerFactory, so use it. + KeyManagerFactory kmf = PlatformDependent.javaVersion() >= 8 ? + SniClientJava8TestUtil.newSniX509KeyManagerFactory(cert, sniHostName) : + SslContext.buildKeyManagerFactory( + new X509Certificate[] { cert.cert() }, cert.key(), null, null); + + sslServerContext = SslContextBuilder.forServer(kmf) + .sslProvider(sslServerProvider) + .build(); + } final Promise promise = group.next().newPromise(); ServerBootstrap sb = new ServerBootstrap(); From 34d52fcbfe2a7e6017267101692e1e29f03896e5 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 14 Sep 2018 20:33:09 +0200 Subject: [PATCH 172/417] Implemented ExtendedOpenSslSession.getStatusResponses() so it not throws an UnsupportedOperationException. (#8290) Motivation: 6ed7c6c75d458047adc37470697f215e9d7436ea added support for ExtendedOpenSslSession but we did not override getStatusResponses(). This lead to test failures on java9. Modifications: Implement ExtendedOpenSslSession.getStatusResponses() so it just returns an empty list. Result: Test pass again on Java9. --- .../java/io/netty/handler/ssl/ExtendedOpenSslSession.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java b/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java index 041e99f2e235..87185db529d3 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java +++ b/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java @@ -24,6 +24,7 @@ import javax.security.cert.X509Certificate; import java.security.Principal; import java.security.cert.Certificate; +import java.util.Collections; import java.util.List; /** @@ -51,6 +52,13 @@ abstract class ExtendedOpenSslSession extends ExtendedSSLSession implements Open @SuppressWarnings({ "unchecked", "rawtypes" }) public abstract List getRequestedServerNames(); + // Do not mark as override so we can compile on java8. + public List getStatusResponses() { + // Just return an empty list for now until we support it as otherwise we will fail in java9 + // because of their sun.security.ssl.X509TrustManagerImpl class. + return Collections.emptyList(); + } + @Override public void handshakeFinished() throws SSLException { wrapped.handshakeFinished(); From 2ab3e13f08e8549c6957aa263390ceb36539b999 Mon Sep 17 00:00:00 2001 From: Andrey Mizurov Date: Fri, 14 Sep 2018 22:39:01 +0300 Subject: [PATCH 173/417] Fix get charset from content-type header with multiple parameters (#8286) Motivation: Get charset from Content-Type header even it contains multiple parameters. Modification: Extract charset value from the charset parameter if it is not last. Result: Fixes #8273 --- .../io/netty/handler/codec/http/HttpUtil.java | 40 +++++++++++++------ .../handler/codec/http/HttpUtilTest.java | 37 ++++++++++++++--- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java index 5b6e90e9abd3..94af7901fc47 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java @@ -15,18 +15,18 @@ */ package io.netty.handler.codec.http; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; - import java.net.InetSocketAddress; import java.net.URI; -import java.util.ArrayList; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import io.netty.util.AsciiString; +import io.netty.util.CharsetUtil; +import io.netty.util.NetUtil; + /** * Utility methods useful in the HTTP context. */ @@ -60,12 +60,13 @@ public static boolean isAsteriskForm(URI uri) { /** * Returns {@code true} if and only if the connection can remain open and * thus 'kept alive'. This methods respects the value of the. + * * {@code "Connection"} header first and then the return value of * {@link HttpVersion#isKeepAliveDefault()}. */ public static boolean isKeepAlive(HttpMessage message) { CharSequence connection = message.headers().get(HttpHeaderNames.CONNECTION); - if (connection != null && HttpHeaderValues.CLOSE.contentEqualsIgnoreCase(connection)) { + if (HttpHeaderValues.CLOSE.contentEqualsIgnoreCase(connection)) { return false; } @@ -193,6 +194,7 @@ public static long getContentLength(HttpMessage message, long defaultValue) { /** * Get an {@code int} representation of {@link #getContentLength(HttpMessage, long)}. + * * @return the content length or {@code defaultValue} if this message does * not have the {@code "Content-Length"} header or its value is not * a number. Not to exceed the boundaries of integer. @@ -313,6 +315,7 @@ public static boolean isTransferEncodingChunked(HttpMessage message) { /** * Set the {@link HttpHeaderNames#TRANSFER_ENCODING} to either include {@link HttpHeaderValues#CHUNKED} if * {@code chunked} is {@code true}, or remove {@link HttpHeaderValues#CHUNKED} if {@code chunked} is {@code false}. + * * @param m The message which contains the headers to modify. * @param chunked if {@code true} then include {@link HttpHeaderValues#CHUNKED} in the headers. otherwise remove * {@link HttpHeaderValues#CHUNKED} from the headers. @@ -371,7 +374,7 @@ public static Charset getCharset(CharSequence contentTypeValue) { /** * Fetch charset from message's Content-Type header. * - * @param message entity to fetch Content-Type header from + * @param message entity to fetch Content-Type header from * @param defaultCharset result to use in case of empty, incorrect or doesn't contain required part header value * @return the charset from message's Content-Type header or {@code defaultCharset} * if charset is not presented or unparsable @@ -389,7 +392,7 @@ public static Charset getCharset(HttpMessage message, Charset defaultCharset) { * Fetch charset from Content-Type header value. * * @param contentTypeValue Content-Type header value to parse - * @param defaultCharset result to use in case of empty, incorrect or doesn't contain required part header value + * @param defaultCharset result to use in case of empty, incorrect or doesn't contain required part header value * @return the charset from message's Content-Type header or {@code defaultCharset} * if charset is not presented or unparsable */ @@ -459,13 +462,23 @@ public static CharSequence getCharsetAsSequence(CharSequence contentTypeValue) { if (contentTypeValue == null) { throw new NullPointerException("contentTypeValue"); } + int indexOfCharset = AsciiString.indexOfIgnoreCaseAscii(contentTypeValue, CHARSET_EQUALS, 0); - if (indexOfCharset != AsciiString.INDEX_NOT_FOUND) { - int indexOfEncoding = indexOfCharset + CHARSET_EQUALS.length(); - if (indexOfEncoding < contentTypeValue.length()) { - return contentTypeValue.subSequence(indexOfEncoding, contentTypeValue.length()); + if (indexOfCharset == AsciiString.INDEX_NOT_FOUND) { + return null; + } + + int indexOfEncoding = indexOfCharset + CHARSET_EQUALS.length(); + if (indexOfEncoding < contentTypeValue.length()) { + CharSequence charsetCandidate = contentTypeValue.subSequence(indexOfEncoding, contentTypeValue.length()); + int indexOfSemicolon = AsciiString.indexOfIgnoreCaseAscii(charsetCandidate, SEMICOLON, 0); + if (indexOfSemicolon == AsciiString.INDEX_NOT_FOUND) { + return charsetCandidate; } + + return charsetCandidate.subSequence(0, indexOfSemicolon); } + return null; } @@ -517,6 +530,7 @@ public static CharSequence getMimeType(CharSequence contentTypeValue) { /** * Formats the host string of an address so it can be used for computing an HTTP component * such as an URL or a Host header + * * @param addr the address * @return the formatted String */ @@ -526,7 +540,7 @@ public static String formatHostnameForHttp(InetSocketAddress addr) { if (!addr.isUnresolved()) { hostString = NetUtil.toAddressString(addr.getAddress()); } - return "[" + hostString + "]"; + return '[' + hostString + ']'; } return hostString; } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpUtilTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpUtilTest.java index 6ad08e6b4c88..31596067aefe 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpUtilTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpUtilTest.java @@ -15,10 +15,6 @@ */ package io.netty.handler.codec.http; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import org.junit.Test; - import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; @@ -26,12 +22,14 @@ import java.util.Collections; import java.util.List; +import io.netty.util.CharsetUtil; +import io.netty.util.ReferenceCountUtil; +import org.junit.Test; + import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static org.hamcrest.Matchers.hasToString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -91,6 +89,22 @@ public void testGetCharset() { assertEquals(CharsetUtil.UTF_8, HttpUtil.getCharset(UPPER_CASE_NORMAL_CONTENT_TYPE)); } + @Test + public void testGetCharsetIfNotLastParameter() { + String NORMAL_CONTENT_TYPE_WITH_PARAMETERS = "application/soap-xml; charset=utf-8; " + + "action=\"http://www.soap-service.by/foo/add\""; + + HttpMessage message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, + "http://localhost:7788/foo"); + message.headers().set(HttpHeaderNames.CONTENT_TYPE, NORMAL_CONTENT_TYPE_WITH_PARAMETERS); + + assertEquals(CharsetUtil.UTF_8, HttpUtil.getCharset(message)); + assertEquals(CharsetUtil.UTF_8, HttpUtil.getCharset(NORMAL_CONTENT_TYPE_WITH_PARAMETERS)); + + assertEquals("utf-8", HttpUtil.getCharsetAsSequence(message)); + assertEquals("utf-8", HttpUtil.getCharsetAsSequence(NORMAL_CONTENT_TYPE_WITH_PARAMETERS)); + } + @Test public void testGetCharset_defaultValue() { final String SIMPLE_CONTENT_TYPE = "text/html"; @@ -292,4 +306,15 @@ public void testIpv4Unresolved() { InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("10.0.0.1", 8080); assertEquals("10.0.0.1", HttpUtil.formatHostnameForHttp(socketAddress)); } + + @Test + public void testKeepAliveIfConnectionHeaderAbsent() { + HttpMessage http11Message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, + "http:localhost/http_1_1"); + assertTrue(HttpUtil.isKeepAlive(http11Message)); + + HttpMessage http10Message = new DefaultHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, + "http:localhost/http_1_0"); + assertFalse(HttpUtil.isKeepAlive(http10Message)); + } } From 687275361f96076c80787af4617882eb2bc6e9d2 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 18 Sep 2018 14:19:06 -0700 Subject: [PATCH 174/417] Update to Conscrypt 1.3.0 (#8296) Motivation: Conscrypt 1.3.0 was just released and adds support for TLSv1.3 Modifications: Update to 1.3.0 Result: Use latest conscrypt during build / test. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 702828a86da0..305e9fdaafd4 100644 --- a/pom.xml +++ b/pom.xml @@ -225,7 +225,7 @@ ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber - 1.1.3 + 1.3.0 ${os.detected.name}-${os.detected.arch} ${project.basedir}/../common/src/test/resources/logback-test.xml From 1b6e47ab2be03cb0d1cafcdf3862ed6591636e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Kautler?= Date: Tue, 18 Sep 2018 23:20:28 +0200 Subject: [PATCH 175/417] Fix incorrectly encoded empty SOCKS5 address (#8292) Motivation: If you encode a SOCKS5 message like new DefaultSocks5CommandResponse(FAILURE, DOMAIN, "", 0) you correctly get a result of 05010003000000. But if the bndAddr is null, for example like new DefaultSocks5CommandResponse(FAILURE, DOMAIN) the encoded result is 0501000301000000 which means the domain name has a length of one and consists of a 0-byte. Modification: With this commit it is also correctly encoded as a string of 0 length. Result: Correctly encode empty SOCKS5 address --- .../io/netty/handler/codec/socksx/v5/Socks5AddressEncoder.java | 1 - .../codec/socksx/v5/DefaultSocks5CommandResponseTest.java | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AddressEncoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AddressEncoder.java index fcd29cf3f3fa..390622caba77 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AddressEncoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AddressEncoder.java @@ -44,7 +44,6 @@ public void encodeAddress(Socks5AddressType addrType, String addrValue, ByteBuf out.writeByte(addrValue.length()); out.writeCharSequence(addrValue, CharsetUtil.US_ASCII); } else { - out.writeByte(1); out.writeByte(0); } } else if (typeVal == Socks5AddressType.IPv6.byteValue()) { diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandResponseTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandResponseTest.java index 36fea750275f..9da123602e7b 100755 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandResponseTest.java +++ b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandResponseTest.java @@ -51,8 +51,7 @@ public void testEmptyDomain() { 0x00, // success reply 0x00, // reserved 0x03, // address type domain - 0x01, // length of domain - 0x00, // domain value + 0x00, // length of domain 0x00, // port value 0x00 }; From 01db30a163819b34cd9f96fb40526c93b48e7534 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 19 Sep 2018 17:13:44 -0700 Subject: [PATCH 176/417] Correctly implement ExtendedSSLSession.getStatusResponses() for ReferenceCountedOpenSslEngine (#8297) Motivation: Java9 added getStatusResponses() to ExtendedSSLSession which we should correctly support when possible. Modifications: Implement the method correctly. Result: More complete and correct implementation. --- .../handler/ssl/ReferenceCountedOpenSslEngine.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 1d608eeeb7fb..6058bf16a7ab 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -251,6 +251,20 @@ protected void deallocate() { public List getRequestedServerNames() { return Java8SslUtils.getSniHostNames(sniHostNames); } + + @Override + public List getStatusResponses() { + byte[] ocspResponse = null; + if (enableOcsp && clientMode) { + synchronized (ReferenceCountedOpenSslEngine.this) { + if (!isDestroyed()) { + ocspResponse = SSL.getOcspResponse(ssl); + } + } + } + return ocspResponse == null ? + Collections.emptyList() : Collections.singletonList(ocspResponse); + } }; } else { session = new DefaultOpenSslSession(context.sessionContext()); From a80c49828f8d52b70e68b8a4c80f677ca44af0af Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 21 Sep 2018 08:04:24 -0700 Subject: [PATCH 177/417] Cleanup SSL test. (#8301) Motivation: I noticed that we had some errors showing up in a test (which did not fail it tho) because we tried to full-fill the promise multiples times. Modifications: Use trySuccess(...) as we may produce multiple exceptions. Result: Less errors during test-run. --- .../java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java index 813e59937281..2abc33e8a310 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java @@ -340,7 +340,7 @@ protected void initChannel(Channel ch) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { if (cause.getCause() instanceof SSLException) { // We received the alert and so produce an SSLException. - promise.setSuccess(null); + promise.trySuccess(null); } } }); From 9a3be347af35107201f7d4a72416e76cf6b39a82 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 22 Sep 2018 13:34:12 -0700 Subject: [PATCH 178/417] Ensure we always encode all data in JdkZlibEncoder. (#8305) Motivation: In theory our estimation of the needed buffer could be off and so we need to ensure we grow it if there is no space left. Modifications: Ensure we grow the buffer if there is no space left in there but we still have data to deflate. Result: Correctly deflate data in all cases. --- .../handler/codec/compression/JdkZlibEncoder.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java index f039fa66e849..276d7f86b0cd 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java @@ -225,8 +225,18 @@ protected void encode(ChannelHandlerContext ctx, ByteBuf uncompressed, ByteBuf o } deflater.setInput(inAry, offset, len); - while (!deflater.needsInput()) { + for (;;) { deflate(out); + if (deflater.needsInput()) { + // Consumed everything + break; + } else { + if (!out.isWritable()) { + // We did not consume everything but the buffer is not writable anymore. Increase the capacity to + // make more room. + out.ensureWritable(out.writerIndex()); + } + } } } From 60a7ece4c3037cc5f9a76cf22ecb57dad2563d74 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 26 Sep 2018 12:16:19 +0200 Subject: [PATCH 179/417] Update to final Java11 release (#8320) Motivation: We should use final Java11 release during builds. Modifications: Update to final Java11 release Result: Use latest release. --- docker/docker-compose.centos-6.111.yaml | 2 +- docker/docker-compose.centos-7.111.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml index 346fe72e0e7a..9950fbf18973 100644 --- a/docker/docker-compose.centos-6.111.yaml +++ b/docker/docker-compose.centos-6.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "1.11.0-28" + java_version : "1.11.0" test: image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-7.111.yaml b/docker/docker-compose.centos-7.111.yaml index 5bd4a542e7fa..975822c3534f 100644 --- a/docker/docker-compose.centos-7.111.yaml +++ b/docker/docker-compose.centos-7.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "1.11.0-28" + java_version : "1.11.0" test: image: netty:centos-7-1.11 From 4d1458604ab3d378192c6240dd12ea85d1223838 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 26 Sep 2018 19:59:00 +0200 Subject: [PATCH 180/417] Fix leak in SniClientTest. (#8324) Motivation: We need to release the ReferenceCountedSslContext to eliminate resource leaks. Reported in https://garage.netty.io/teamcity/viewLog.html?buildId=33353&buildTypeId=netty_build_oraclejdk8&tab=buildLog#_focus=157264. Modifications: Call release on the SslContext instances. Result: No more leaks in tests. --- .../io/netty/handler/ssl/SniClientTest.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java index 238594bbf44c..7cb473150f66 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java @@ -28,6 +28,8 @@ import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.util.Mapping; +import io.netty.util.ReferenceCountUtil; +import io.netty.util.ReferenceCounted; import io.netty.util.concurrent.Promise; import io.netty.util.internal.PlatformDependent; import org.junit.Assert; @@ -95,12 +97,13 @@ private static void testSniClient(SslProvider sslServerProvider, SslProvider ssl String sniHostName = "sni.netty.io"; LocalAddress address = new LocalAddress("test"); EventLoopGroup group = new DefaultEventLoopGroup(1); + SelfSignedCertificate cert = new SelfSignedCertificate(); + SslContext sslServerContext = null; + SslContext sslClientContext = null; + Channel sc = null; Channel cc = null; try { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - final SslContext sslServerContext; if ((sslServerProvider == SslProvider.OPENSSL || sslServerProvider == SslProvider.OPENSSL_REFCNT) && !OpenSsl.useKeyManagerFactory()) { sslServerContext = SslContextBuilder.forServer(cert.certificate(), cert.privateKey()) @@ -118,6 +121,7 @@ private static void testSniClient(SslProvider sslServerProvider, SslProvider ssl .build(); } + final SslContext finalContext = sslServerContext; final Promise promise = group.next().newPromise(); ServerBootstrap sb = new ServerBootstrap(); sc = sb.group(group).channel(LocalServerChannel.class).childHandler(new ChannelInitializer() { @@ -127,7 +131,7 @@ protected void initChannel(Channel ch) throws Exception { @Override public SslContext map(String input) { promise.setSuccess(input); - return sslServerContext; + return finalContext; } })); } @@ -136,12 +140,12 @@ public SslContext map(String input) { TrustManagerFactory tmf = PlatformDependent.javaVersion() >= 8 ? SniClientJava8TestUtil.newSniX509TrustmanagerFactory(sniHostName) : InsecureTrustManagerFactory.INSTANCE; - SslContext sslContext = SslContextBuilder.forClient().trustManager(tmf) + sslClientContext = SslContextBuilder.forClient().trustManager(tmf) .sslProvider(sslClientProvider).build(); Bootstrap cb = new Bootstrap(); SslHandler handler = new SslHandler( - sslContext.newEngine(ByteBufAllocator.DEFAULT, sniHostName, -1)); + sslClientContext.newEngine(ByteBufAllocator.DEFAULT, sniHostName, -1)); cc = cb.group(group).channel(LocalChannel.class).handler(handler) .connect(address).syncUninterruptibly().channel(); Assert.assertEquals(sniHostName, promise.syncUninterruptibly().getNow()); @@ -160,6 +164,11 @@ public SslContext map(String input) { if (sc != null) { sc.close().syncUninterruptibly(); } + ReferenceCountUtil.release(sslServerContext); + ReferenceCountUtil.release(sslClientContext); + + cert.delete(); + group.shutdownGracefully(); } } From 618a98fdb5deeeed250bcd1c044ef52518c5a43f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 26 Sep 2018 20:01:53 +0200 Subject: [PATCH 181/417] Add profile to be able to compile on java12 (#8321) Motivation: First EA releases of Java12 are out we should be able to compile with these and run tests. Modifications: Add maven profile for java12. Result: Be able to use Java12 --- pom.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pom.xml b/pom.xml index 305e9fdaafd4..3670a6fb2db3 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,26 @@ + + + java12 + + 12 + + + + + true + + 3.0.0-M1 + + 2.0.5.Final + + 1.7 + 1.7 + + + java11 From ba594bcf4a62c47810f85c6d28e87367c6903ed4 Mon Sep 17 00:00:00 2001 From: Matt Ayres Date: Wed, 26 Sep 2018 11:55:46 -0700 Subject: [PATCH 182/417] Fixed illegal reflective access by not relying on a sun.net.dns class. (#8318) (#8319) Motivation Applications should not depend on internal packages with Java 9 and later. This cause a warning now, but will break in future versions of Java. Modification This change adds methods to UnixResolverDnsServerAddressStreamProvider (following after #6844) that parse /etc/resolv.conf for domain and search entries. Then DnsNameResolver does not need to rely on sun.net.dns.ResolverConfiguration to do this. Result Fixes #8318. Furthermore, at least in my testing with Java 11, this also makes multiple search entries work properly (previously I was only getting the first entry). --- .../netty/resolver/dns/DnsNameResolver.java | 22 +++++--- ...esolverDnsServerAddressStreamProvider.java | 56 +++++++++++++++++++ ...verDnsServerAddressStreamProviderTest.java | 37 ++++++++++++ 3 files changed, 108 insertions(+), 7 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java index 63df0e8421c9..d9345c24b2ff 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java @@ -125,13 +125,9 @@ public class DnsNameResolver extends InetNameResolver { static { String[] searchDomains; try { - Class configClass = Class.forName("sun.net.dns.ResolverConfiguration"); - Method open = configClass.getMethod("open"); - Method nameservers = configClass.getMethod("searchlist"); - Object instance = open.invoke(null); - - @SuppressWarnings("unchecked") - List list = (List) nameservers.invoke(instance); + List list = PlatformDependent.isWindows() + ? getSearchDomainsHack() + : UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(); searchDomains = list.toArray(new String[0]); } catch (Exception ignore) { // Failed to get the system name search domain list. @@ -148,6 +144,18 @@ public class DnsNameResolver extends InetNameResolver { DEFAULT_NDOTS = ndots; } + @SuppressWarnings("unchecked") + private static List getSearchDomainsHack() throws Exception { + // This code on Java 9+ yields a warning about illegal reflective access that will be denied in + // a future release. There doesn't seem to be a better way to get search domains for Windows yet. + Class configClass = Class.forName("sun.net.dns.ResolverConfiguration"); + Method open = configClass.getMethod("open"); + Method nameservers = configClass.getMethod("searchlist"); + Object instance = open.invoke(null); + + return (List) nameservers.invoke(instance); + } + private static final DatagramDnsResponseDecoder DECODER = new DatagramDnsResponseDecoder(); private static final DatagramDnsQueryEncoder ENCODER = new DatagramDnsQueryEncoder(); diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java index d5571c93a977..97a3a90d6b61 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java @@ -27,7 +27,9 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -51,6 +53,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ private static final String SORTLIST_ROW_LABEL = "sortlist"; private static final String OPTIONS_ROW_LABEL = "options"; private static final String DOMAIN_ROW_LABEL = "domain"; + private static final String SEARCH_ROW_LABEL = "search"; private static final String PORT_ROW_LABEL = "port"; private static final String NDOTS_LABEL = "ndots:"; static final int DEFAULT_NDOTS = 1; @@ -284,4 +287,57 @@ static int parseEtcResolverFirstNdots(File etcResolvConf) throws IOException { } return DEFAULT_NDOTS; } + + /** + * Parse a file of the format /etc/resolv.conf and return the + * list of search domains found in it or an empty list if not found. + * @return List of search domains. + * @throws IOException If a failure occurs parsing the file. + */ + static List parseEtcResolverSearchDomains() throws IOException { + return parseEtcResolverSearchDomains(new File(ETC_RESOLV_CONF_FILE)); + } + + /** + * Parse a file of the format /etc/resolv.conf and return the + * list of search domains found in it or an empty list if not found. + * @param etcResolvConf a file of the format /etc/resolv.conf. + * @return List of search domains. + * @throws IOException If a failure occurs parsing the file. + */ + static List parseEtcResolverSearchDomains(File etcResolvConf) throws IOException { + String localDomain = null; + List searchDomains = new ArrayList(); + + FileReader fr = new FileReader(etcResolvConf); + BufferedReader br = null; + try { + br = new BufferedReader(fr); + String line; + while ((line = br.readLine()) != null) { + if (localDomain == null && line.startsWith(DOMAIN_ROW_LABEL)) { + int i = indexOfNonWhiteSpace(line, DOMAIN_ROW_LABEL.length()); + if (i >= 0) { + localDomain = line.substring(i); + } + } else if (line.startsWith(SEARCH_ROW_LABEL)) { + int i = indexOfNonWhiteSpace(line, SEARCH_ROW_LABEL.length()); + if (i >= 0) { + searchDomains.add(line.substring(i)); + } + } + } + } finally { + if (br == null) { + fr.close(); + } else { + br.close(); + } + } + + // return what was on the 'domain' line only if there were no 'search' lines + return localDomain != null && searchDomains.isEmpty() + ? Collections.singletonList(localDomain) + : searchDomains; + } } diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java index 38c6c919c512..e428109412d6 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java @@ -25,6 +25,9 @@ import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import static io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.DEFAULT_NDOTS; import static io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.parseEtcResolverFirstNdots; @@ -111,6 +114,40 @@ public void emptyEtcResolverDirectoryDoesNotThrow() throws IOException { assertHostNameEquals("127.0.0.2", stream.next()); } + @Test + public void searchDomainsWithOnlyDomain() throws IOException { + File f = buildFile("domain linecorp.local\n" + + "nameserver 127.0.0.2\n"); + List domains = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(f); + assertEquals(Collections.singletonList("linecorp.local"), domains); + } + + @Test + public void searchDomainsWithOnlySearch() throws IOException { + File f = buildFile("search linecorp.local\n" + + "nameserver 127.0.0.2\n"); + List domains = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(f); + assertEquals(Collections.singletonList("linecorp.local"), domains); + } + + @Test + public void searchDomainsWithMultipleSearch() throws IOException { + File f = buildFile("search linecorp.local\n" + + "search squarecorp.local\n" + + "nameserver 127.0.0.2\n"); + List domains = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(f); + assertEquals(Arrays.asList("linecorp.local", "squarecorp.local"), domains); + } + + @Test + public void searchDomainsPrecedence() throws IOException { + File f = buildFile("domain linecorp.local\n" + + "search squarecorp.local\n" + + "nameserver 127.0.0.2\n"); + List domains = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(f); + assertEquals(Collections.singletonList("squarecorp.local"), domains); + } + private File buildFile(String contents) throws IOException { File f = folder.newFile(); OutputStream out = new FileOutputStream(f); From c546ab20a1721ae98142398e187c2c4a3c07d41a Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 27 Sep 2018 07:38:42 +0200 Subject: [PATCH 183/417] Ensure ByteToMessageDecoder.Cumulator implementations always release in buffer. (#8325) Motivation: We need to ensure the Cumulator always releases the input buffer if it can not take over the ownership of it as otherwise it may leak. Modifications: - Correctly ensure the buffer is always released. - Add unit tests. Result: Ensure buffer is always released. --- .../handler/codec/ByteToMessageDecoder.java | 81 +++++++++++-------- .../codec/ByteToMessageDecoderTest.java | 48 ++++++++++- 2 files changed, 91 insertions(+), 38 deletions(-) diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java index 9d52b5506e13..fec73f8fc9a3 100644 --- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java @@ -75,23 +75,28 @@ public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter public static final Cumulator MERGE_CUMULATOR = new Cumulator() { @Override public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) { - final ByteBuf buffer; - if (cumulation.writerIndex() > cumulation.maxCapacity() - in.readableBytes() + try { + final ByteBuf buffer; + if (cumulation.writerIndex() > cumulation.maxCapacity() - in.readableBytes() || cumulation.refCnt() > 1 || cumulation.isReadOnly()) { - // Expand cumulation (by replace it) when either there is not more room in the buffer - // or if the refCnt is greater then 1 which may happen when the user use slice().retain() or - // duplicate().retain() or if its read-only. - // - // See: - // - https://github.com/netty/netty/issues/2327 - // - https://github.com/netty/netty/issues/1764 - buffer = expandCumulation(alloc, cumulation, in.readableBytes()); - } else { - buffer = cumulation; + // Expand cumulation (by replace it) when either there is not more room in the buffer + // or if the refCnt is greater then 1 which may happen when the user use slice().retain() or + // duplicate().retain() or if its read-only. + // + // See: + // - https://github.com/netty/netty/issues/2327 + // - https://github.com/netty/netty/issues/1764 + buffer = expandCumulation(alloc, cumulation, in.readableBytes()); + } else { + buffer = cumulation; + } + buffer.writeBytes(in); + return buffer; + } finally { + // We must release in in all cases as otherwise it may produce a leak if writeBytes(...) throw + // for whatever release (for example because of OutOfMemoryError) + in.release(); } - buffer.writeBytes(in); - in.release(); - return buffer; } }; @@ -104,28 +109,36 @@ public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) @Override public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) { ByteBuf buffer; - if (cumulation.refCnt() > 1) { - // Expand cumulation (by replace it) when the refCnt is greater then 1 which may happen when the user - // use slice().retain() or duplicate().retain(). - // - // See: - // - https://github.com/netty/netty/issues/2327 - // - https://github.com/netty/netty/issues/1764 - buffer = expandCumulation(alloc, cumulation, in.readableBytes()); - buffer.writeBytes(in); - in.release(); - } else { - CompositeByteBuf composite; - if (cumulation instanceof CompositeByteBuf) { - composite = (CompositeByteBuf) cumulation; + try { + if (cumulation.refCnt() > 1) { + // Expand cumulation (by replace it) when the refCnt is greater then 1 which may happen when the + // user use slice().retain() or duplicate().retain(). + // + // See: + // - https://github.com/netty/netty/issues/2327 + // - https://github.com/netty/netty/issues/1764 + buffer = expandCumulation(alloc, cumulation, in.readableBytes()); + buffer.writeBytes(in); } else { - composite = alloc.compositeBuffer(Integer.MAX_VALUE); - composite.addComponent(true, cumulation); + CompositeByteBuf composite; + if (cumulation instanceof CompositeByteBuf) { + composite = (CompositeByteBuf) cumulation; + } else { + composite = alloc.compositeBuffer(Integer.MAX_VALUE); + composite.addComponent(true, cumulation); + } + composite.addComponent(true, in); + in = null; + buffer = composite; + } + return buffer; + } finally { + if (in != null) { + // We must release if the ownership was not transfered as otherwise it may produce a leak if + // writeBytes(...) throw for whatever release (for example because of OutOfMemoryError). + in.release(); } - composite.addComponent(true, in); - buffer = composite; } - return buffer; } }; diff --git a/codec/src/test/java/io/netty/handler/codec/ByteToMessageDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/ByteToMessageDecoderTest.java index 8462ee4a020c..93dd2218ac0f 100644 --- a/codec/src/test/java/io/netty/handler/codec/ByteToMessageDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/ByteToMessageDecoderTest.java @@ -16,7 +16,10 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; +import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.Unpooled; +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.buffer.UnpooledHeapByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.embedded.EmbeddedChannel; @@ -27,10 +30,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class ByteToMessageDecoderTest { @@ -305,4 +305,44 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) t assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(new byte[] { (byte) 2 }))); assertFalse(channel.finish()); } + + @Test + public void releaseWhenMergeCumulateThrows() { + final Error error = new Error(); + + ByteBuf cumulation = new UnpooledHeapByteBuf(UnpooledByteBufAllocator.DEFAULT, 0, 64) { + @Override + public ByteBuf writeBytes(ByteBuf src) { + throw error; + } + }; + ByteBuf in = Unpooled.buffer().writeZero(12); + try { + ByteToMessageDecoder.MERGE_CUMULATOR.cumulate(UnpooledByteBufAllocator.DEFAULT, cumulation, in); + fail(); + } catch (Error expected) { + assertSame(error, expected); + assertEquals(0, in.refCnt()); + } + } + + @Test + public void releaseWhenCompositeCumulateThrows() { + final Error error = new Error(); + + ByteBuf cumulation = new CompositeByteBuf(UnpooledByteBufAllocator.DEFAULT, false, 64) { + @Override + public CompositeByteBuf addComponent(boolean increaseWriterIndex, ByteBuf buffer) { + throw error; + } + }; + ByteBuf in = Unpooled.buffer().writeZero(12); + try { + ByteToMessageDecoder.COMPOSITE_CUMULATOR.cumulate(UnpooledByteBufAllocator.DEFAULT, cumulation, in); + fail(); + } catch (Error expected) { + assertSame(error, expected); + assertEquals(0, in.refCnt()); + } + } } From 9acd9273c14bd8a5364f749b51f9dacefb6b7e99 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 27 Sep 2018 09:13:28 +0200 Subject: [PATCH 184/417] Allow to run tests with leak detection enabled. (#8323) Motivation: We should add some command to be able to run all tests with leak detection enabled. This will then be used on the CI during PR builds. Modifications: Add new docker-compose config to run with leak-detection enabled. Result: Easy way to enable leak detection while running tests via docker. --- docker/docker-compose.centos-6.110.yaml | 2 ++ docker/docker-compose.centos-6.111.yaml | 3 +++ docker/docker-compose.centos-6.18.yaml | 3 +++ docker/docker-compose.centos-6.19.yaml | 3 +++ docker/docker-compose.centos-7.110.yaml | 3 +++ docker/docker-compose.centos-7.111.yaml | 3 +++ docker/docker-compose.centos-7.18.yaml | 3 +++ docker/docker-compose.centos-7.19.yaml | 3 +++ docker/docker-compose.yaml | 4 ++++ 9 files changed, 27 insertions(+) diff --git a/docker/docker-compose.centos-6.110.yaml b/docker/docker-compose.centos-6.110.yaml index 1d96010f447b..0db4cfd83787 100644 --- a/docker/docker-compose.centos-6.110.yaml +++ b/docker/docker-compose.centos-6.110.yaml @@ -11,6 +11,8 @@ services: test: image: netty:centos-6-1.10 + test-leak: + image: netty:centos-6-1.10 test-boringssl-static: image: netty:centos-6-1.10 diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml index 9950fbf18973..47ddec15f2ab 100644 --- a/docker/docker-compose.centos-6.111.yaml +++ b/docker/docker-compose.centos-6.111.yaml @@ -12,6 +12,9 @@ services: test: image: netty:centos-6-1.11 + test-leak: + image: netty:centos-6-1.11 + test-boringssl-static: image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-6.18.yaml b/docker/docker-compose.centos-6.18.yaml index 986e3fb2be80..61b2b6edb1c6 100644 --- a/docker/docker-compose.centos-6.18.yaml +++ b/docker/docker-compose.centos-6.18.yaml @@ -12,6 +12,9 @@ services: test: image: netty:centos-6-1.8 + test-leak: + image: netty:centos-6-1.8 + test-boringssl-static: image: netty:centos-6-1.8 diff --git a/docker/docker-compose.centos-6.19.yaml b/docker/docker-compose.centos-6.19.yaml index 54f32b9ef5eb..861d9d9e7652 100644 --- a/docker/docker-compose.centos-6.19.yaml +++ b/docker/docker-compose.centos-6.19.yaml @@ -12,6 +12,9 @@ services: test: image: netty:centos-6-1.9 + test-leak: + image: netty:centos-6-1.9 + test-boringssl-static: image: netty:centos-6-1.9 diff --git a/docker/docker-compose.centos-7.110.yaml b/docker/docker-compose.centos-7.110.yaml index d823f4bcbb52..d2004a1b1d82 100644 --- a/docker/docker-compose.centos-7.110.yaml +++ b/docker/docker-compose.centos-7.110.yaml @@ -12,6 +12,9 @@ services: test: image: netty:centos-7-1.10 + test-leak: + image: netty:centos-7-1.10 + test-boringssl-static: image: netty:centos-7-1.10 diff --git a/docker/docker-compose.centos-7.111.yaml b/docker/docker-compose.centos-7.111.yaml index 975822c3534f..517f9334429a 100644 --- a/docker/docker-compose.centos-7.111.yaml +++ b/docker/docker-compose.centos-7.111.yaml @@ -12,6 +12,9 @@ services: test: image: netty:centos-7-1.11 + test-leak: + image: netty:centos-7-1.11 + test-boringssl-static: image: netty:centos-7-1.11 diff --git a/docker/docker-compose.centos-7.18.yaml b/docker/docker-compose.centos-7.18.yaml index 31c8d5127dd6..df3aa0040aee 100644 --- a/docker/docker-compose.centos-7.18.yaml +++ b/docker/docker-compose.centos-7.18.yaml @@ -12,6 +12,9 @@ services: test: image: netty:centos-7-1.8 + test-leak: + image: netty:centos-7-1.8 + test-boringssl-static: image: netty:centos-7-1.8 diff --git a/docker/docker-compose.centos-7.19.yaml b/docker/docker-compose.centos-7.19.yaml index 3f41d6354d54..3b6187538904 100644 --- a/docker/docker-compose.centos-7.19.yaml +++ b/docker/docker-compose.centos-7.19.yaml @@ -12,6 +12,9 @@ services: test: image: netty:centos-7-1.9 + test-leak: + image: netty:centos-7-1.9 + test-boringssl-static: image: netty:centos-7-1.9 diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 834f6ce1d98a..ceb5a8791c65 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -17,6 +17,10 @@ services: - ..:/code working_dir: /code + test-leak: + <<: *common + command: /bin/bash -cl "./mvnw -Pleak clean install -Dio.netty.testsuite.badHost=netty.io" + test: <<: *common command: /bin/bash -cl "./mvnw clean install -Dio.netty.testsuite.badHost=netty.io" From 652650e127dbabbcfd7b41d4bd0991a7ad5f87ad Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 27 Sep 2018 09:32:41 +0200 Subject: [PATCH 185/417] Fix leak in SniClientJava8TestUtil (#8326) Motivation: 4d1458604ab3d378192c6240dd12ea85d1223838 did fix some leaks in SniClientTest but missed the ones in SniClientJava8TestUtil. Modifications: Correctly release SslContext. Result: No more leaks in SNI tests. --- .../handler/ssl/SniClientJava8TestUtil.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java index c6d05d05c71c..3c7783785ef4 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java @@ -30,6 +30,7 @@ import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SimpleTrustManagerFactory; +import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.Promise; import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.ThrowableUtil; @@ -76,20 +77,25 @@ private SniClientJava8TestUtil() { } static void testSniClient(SslProvider sslClientProvider, SslProvider sslServerProvider, final boolean match) throws Exception { final String sniHost = "sni.netty.io"; + SelfSignedCertificate cert = new SelfSignedCertificate(); LocalAddress address = new LocalAddress("test"); EventLoopGroup group = new DefaultEventLoopGroup(1); + SslContext sslServerContext = null; + SslContext sslClientContext = null; + Channel sc = null; Channel cc = null; try { - SelfSignedCertificate cert = new SelfSignedCertificate(); - final SslContext sslServerContext = SslContextBuilder.forServer(cert.key(), cert.cert()) + sslServerContext = SslContextBuilder.forServer(cert.key(), cert.cert()) .sslProvider(sslServerProvider).build(); final Promise promise = group.next().newPromise(); ServerBootstrap sb = new ServerBootstrap(); + + final SslContext finalContext = sslServerContext; sc = sb.group(group).channel(LocalServerChannel.class).childHandler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { - SslHandler handler = sslServerContext.newHandler(ch.alloc()); + SslHandler handler = finalContext.newHandler(ch.alloc()); SSLParameters parameters = handler.engine().getSSLParameters(); SNIMatcher matcher = new SNIMatcher(0) { @Override @@ -132,11 +138,11 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc } }).bind(address).syncUninterruptibly().channel(); - SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE) + sslClientContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider).build(); SslHandler sslHandler = new SslHandler( - sslContext.newEngine(ByteBufAllocator.DEFAULT, sniHost, -1)); + sslClientContext.newEngine(ByteBufAllocator.DEFAULT, sniHost, -1)); Bootstrap cb = new Bootstrap(); cc = cb.group(group).channel(LocalChannel.class).handler(sslHandler) .connect(address).syncUninterruptibly().channel(); @@ -150,6 +156,12 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc if (sc != null) { sc.close().syncUninterruptibly(); } + + ReferenceCountUtil.release(sslServerContext); + ReferenceCountUtil.release(sslClientContext); + + cert.delete(); + group.shutdownGracefully(); } } From 70efd25801eed5a2ba37828bd681bd71976853bb Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 27 Sep 2018 12:47:17 +0200 Subject: [PATCH 186/417] Add docker-compose config to run with Java12 (#8327) Motivation: The first EA builds for Java12 are released so we should allow to run with these in our docker-compose setup. Modifications: Add docker-compose configs for Java12. Result: Be able to run easily with Java12 as well. --- docker/docker-compose.centos-6.112.yaml | 22 ++++++++++++++++++++++ docker/docker-compose.centos-7.112.yaml | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 docker/docker-compose.centos-6.112.yaml create mode 100644 docker/docker-compose.centos-7.112.yaml diff --git a/docker/docker-compose.centos-6.112.yaml b/docker/docker-compose.centos-6.112.yaml new file mode 100644 index 000000000000..86a97a5ce824 --- /dev/null +++ b/docker/docker-compose.centos-6.112.yaml @@ -0,0 +1,22 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-6-1.12 + build: + args: + centos_version : "6" + java_version : "openjdk@1.12.0" + + test: + image: netty:centos-6-1.12 + + test-leak: + image: netty:centos-6-1.12 + + test-boringssl-static: + image: netty:centos-6-1.12 + + shell: + image: netty:centos-6-1.12 diff --git a/docker/docker-compose.centos-7.112.yaml b/docker/docker-compose.centos-7.112.yaml new file mode 100644 index 000000000000..627e8d34a0e3 --- /dev/null +++ b/docker/docker-compose.centos-7.112.yaml @@ -0,0 +1,22 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-7-1.12 + build: + args: + centos_version : "7" + java_version : "openjdk@1.12.0" + + test: + image: netty:centos-7-1.12 + + test-leak: + image: netty:centos-7-1.12 + + test-boringssl-static: + image: netty:centos-7-1.12 + + shell: + image: netty:centos-7-1.12 From 5650db582607a8eb95022cd73cdc490cc94c0e1c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 27 Sep 2018 17:05:35 +0200 Subject: [PATCH 187/417] Add cache for CNAME mappings resolved during lookup of DNS entries. (#8314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add cache for CNAME mappings resolved during lookup of DNS entries. Motivation: If the CNAMEd hostname is backed by load balancing component, typically the final A or AAAA DNS records have small TTL. However, the CNAME record itself is setup with longer TTL. For example: * x.netty.io could be CNAMEd to y.netty.io with TTL of 5 min * A / AAAA records for y.netty.io has a TTL of 0.5 min In current Netty implementation, original hostname is saved in resolved cached with the TTL of final A / AAAA records. When that cache entry expires, Netty recursive resolver sends at least two queries — 1st one to be resolved as CNAME record and the 2nd one to resolve the hostname in CNAME record. If CNAME record was cached, only the 2nd query would be needed most of the time. 1st query would be needed less frequently. Modifications: Add a new CnameCache that will be used to cache CNAMEs and so may reduce queries. Result: Less queries needed when CNAME is used. --- .../resolver/dns/DefaultDnsCnameCache.java | 99 +++++++++++++ .../io/netty/resolver/dns/DnsCnameCache.java | 59 ++++++++ .../netty/resolver/dns/DnsNameResolver.java | 41 +++++- .../resolver/dns/DnsNameResolverBuilder.java | 22 +++ .../netty/resolver/dns/DnsResolveContext.java | 48 ++++++- .../netty/resolver/dns/NoopDnsCnameCache.java | 47 +++++++ .../dns/DefaultDnsCnameCacheTest.java | 132 ++++++++++++++++++ .../resolver/dns/DnsNameResolverTest.java | 117 ++++++++++++++++ 8 files changed, 557 insertions(+), 8 deletions(-) create mode 100644 resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCnameCache.java create mode 100644 resolver-dns/src/main/java/io/netty/resolver/dns/DnsCnameCache.java create mode 100644 resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsCnameCache.java create mode 100644 resolver-dns/src/test/java/io/netty/resolver/dns/DefaultDnsCnameCacheTest.java diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCnameCache.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCnameCache.java new file mode 100644 index 000000000000..8638808757ab --- /dev/null +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCnameCache.java @@ -0,0 +1,99 @@ +/* + * Copyright 2018 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: + * + * http://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.resolver.dns; + +import io.netty.channel.EventLoop; +import io.netty.util.AsciiString; +import io.netty.util.internal.UnstableApi; + +import java.util.List; + +import static io.netty.util.internal.ObjectUtil.*; + +/** + * Default implementation of a {@link DnsCnameCache}. + */ +@UnstableApi +public final class DefaultDnsCnameCache implements DnsCnameCache { + private final int minTtl; + private final int maxTtl; + + private final Cache cache = new Cache() { + @Override + protected boolean shouldReplaceAll(String entry) { + // Only one 1:1 mapping is supported as specified in the RFC. + return true; + } + + @Override + protected boolean equals(String entry, String otherEntry) { + return AsciiString.contentEqualsIgnoreCase(entry, otherEntry); + } + }; + + /** + * Create a cache that respects the TTL returned by the DNS server. + */ + public DefaultDnsCnameCache() { + this(0, Cache.MAX_SUPPORTED_TTL_SECS); + } + + /** + * Create a cache. + * + * @param minTtl the minimum TTL + * @param maxTtl the maximum TTL + */ + public DefaultDnsCnameCache(int minTtl, int maxTtl) { + this.minTtl = Math.min(Cache.MAX_SUPPORTED_TTL_SECS, checkPositiveOrZero(minTtl, "minTtl")); + this.maxTtl = Math.min(Cache.MAX_SUPPORTED_TTL_SECS, checkPositive(maxTtl, "maxTtl")); + if (minTtl > maxTtl) { + throw new IllegalArgumentException( + "minTtl: " + minTtl + ", maxTtl: " + maxTtl + " (expected: 0 <= minTtl <= maxTtl)"); + } + } + + @SuppressWarnings("unchecked") + @Override + public String get(String hostname) { + checkNotNull(hostname, "hostname"); + List cached = cache.get(hostname); + if (cached == null || cached.isEmpty()) { + return null; + } + // We can never have more then one record. + return cached.get(0); + } + + @Override + public void cache(String hostname, String cname, long originalTtl, EventLoop loop) { + checkNotNull(hostname, "hostname"); + checkNotNull(cname, "cname"); + checkNotNull(loop, "loop"); + cache.cache(hostname, cname, Math.max(minTtl, (int) Math.min(maxTtl, originalTtl)), loop); + } + + @Override + public void clear() { + cache.clear(); + } + + @Override + public boolean clear(String hostname) { + checkNotNull(hostname, "hostname"); + return cache.clear(hostname); + } +} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsCnameCache.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsCnameCache.java new file mode 100644 index 000000000000..820ef67d2459 --- /dev/null +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsCnameCache.java @@ -0,0 +1,59 @@ +/* + * Copyright 2018 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: + * + * http://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.resolver.dns; + +import io.netty.channel.EventLoop; +import io.netty.util.internal.UnstableApi; + +/** + * A cache for {@code CNAME}s. + */ +@UnstableApi +public interface DnsCnameCache { + + /** + * Returns the cached cname for the given hostname. + * + * @param hostname the hostname + * @return the cached entries or an {@code null} if none. + */ + String get(String hostname); + + /** + * Caches a cname entry that should be used for the given hostname. + * + * @param hostname the hostname + * @param cname the cname mapping. + * @param originalTtl the TTL as returned by the DNS server + * @param loop the {@link EventLoop} used to register the TTL timeout + */ + void cache(String hostname, String cname, long originalTtl, EventLoop loop); + + /** + * Clears all cached nameservers. + * + * @see #clear(String) + */ + void clear(); + + /** + * Clears the cached nameservers for the specified hostname. + * + * @return {@code true} if and only if there was an entry for the specified host name in the cache and + * it has been removed by this method + */ + boolean clear(String hostname); +} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java index d9345c24b2ff..069c31c66dc3 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java @@ -174,6 +174,7 @@ private static List getSearchDomainsHack() throws Exception { */ private final DnsCache resolveCache; private final AuthoritativeDnsServerCache authoritativeDnsServerCache; + private final DnsCnameCache cnameCache; private final FastThreadLocal nameServerAddrStream = new FastThreadLocal() { @@ -225,9 +226,7 @@ protected DnsServerAddressStream initialValue() { * @param ndots the ndots value * @param decodeIdn {@code true} if domain / host names should be decoded to unicode when received. * See rfc3492. - * @deprecated Use {@link DnsNameResolver(EventLoop, ChannelFactory, DnsCache, AuthoritativeDnsServerCache, - * DnsQueryLifecycleObserverFactory, long, ResolvedAddressTypes, boolean, int, boolean, int, boolean, - * HostsFileEntriesResolver, DnsServerAddressStreamProvider, String[], int, boolean)} + * @deprecated Use {@link DnsNameResolverBuilder}. */ @Deprecated public DnsNameResolver( @@ -279,7 +278,9 @@ public DnsNameResolver( * @param ndots the ndots value * @param decodeIdn {@code true} if domain / host names should be decoded to unicode when received. * See rfc3492. + * @deprecated Use {@link DnsNameResolverBuilder}. */ + @Deprecated public DnsNameResolver( EventLoop eventLoop, ChannelFactory channelFactory, @@ -298,6 +299,31 @@ public DnsNameResolver( String[] searchDomains, int ndots, boolean decodeIdn) { + this(eventLoop, channelFactory, resolveCache, NoopDnsCnameCache.INSTANCE, authoritativeDnsServerCache, + dnsQueryLifecycleObserverFactory, queryTimeoutMillis, resolvedAddressTypes, recursionDesired, + maxQueriesPerResolve, traceEnabled, maxPayloadSize, optResourceEnabled, hostsFileEntriesResolver, + dnsServerAddressStreamProvider, searchDomains, ndots, decodeIdn); + } + + DnsNameResolver( + EventLoop eventLoop, + ChannelFactory channelFactory, + final DnsCache resolveCache, + final DnsCnameCache cnameCache, + final AuthoritativeDnsServerCache authoritativeDnsServerCache, + DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory, + long queryTimeoutMillis, + ResolvedAddressTypes resolvedAddressTypes, + boolean recursionDesired, + int maxQueriesPerResolve, + boolean traceEnabled, + int maxPayloadSize, + boolean optResourceEnabled, + HostsFileEntriesResolver hostsFileEntriesResolver, + DnsServerAddressStreamProvider dnsServerAddressStreamProvider, + String[] searchDomains, + int ndots, + boolean decodeIdn) { super(eventLoop); this.queryTimeoutMillis = checkPositive(queryTimeoutMillis, "queryTimeoutMillis"); this.resolvedAddressTypes = resolvedAddressTypes != null ? resolvedAddressTypes : DEFAULT_RESOLVE_ADDRESS_TYPES; @@ -309,6 +335,7 @@ public DnsNameResolver( this.dnsServerAddressStreamProvider = checkNotNull(dnsServerAddressStreamProvider, "dnsServerAddressStreamProvider"); this.resolveCache = checkNotNull(resolveCache, "resolveCache"); + this.cnameCache = checkNotNull(cnameCache, "cnameCache"); this.dnsQueryLifecycleObserverFactory = traceEnabled ? dnsQueryLifecycleObserverFactory instanceof NoopDnsQueryLifecycleObserverFactory ? new TraceDnsQueryLifeCycleObserverFactory() : @@ -382,6 +409,7 @@ protected void initChannel(DatagramChannel ch) throws Exception { @Override public void operationComplete(ChannelFuture future) { resolveCache.clear(); + cnameCache.clear(); authoritativeDnsServerCache.clear(); } }); @@ -433,6 +461,13 @@ public DnsCache resolveCache() { return resolveCache; } + /** + * Returns the {@link DnsCnameCache}. + */ + DnsCnameCache cnameCache() { + return cnameCache; + } + /** * Returns the cache used for authoritative DNS servers for a domain. */ diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java index cf6fb88b8d40..06266ccfd36c 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java @@ -40,6 +40,7 @@ public final class DnsNameResolverBuilder { private EventLoop eventLoop; private ChannelFactory channelFactory; private DnsCache resolveCache; + private DnsCnameCache cnameCache; private AuthoritativeDnsServerCache authoritativeDnsServerCache; private Integer minTtl; private Integer maxTtl; @@ -123,6 +124,17 @@ public DnsNameResolverBuilder resolveCache(DnsCache resolveCache) { return this; } + /** + * Sets the cache for {@code CNAME} mappings. + * + * @param cnameCache the cache used to cache {@code CNAME} mappings for a domain. + * @return {@code this} + */ + public DnsNameResolverBuilder cnameCache(DnsCnameCache cnameCache) { + this.cnameCache = cnameCache; + return this; + } + /** * Set the factory used to generate objects which can observe individual DNS queries. * @param lifecycleObserverFactory the factory used to generate objects which can observe individual DNS queries. @@ -376,6 +388,11 @@ private AuthoritativeDnsServerCache newAuthoritativeDnsServerCache() { new NameServerComparator(DnsNameResolver.preferredAddressType(resolvedAddressTypes).addressType())); } + private DnsCnameCache newCnameCache() { + return new DefaultDnsCnameCache( + intValue(minTtl, 0), intValue(maxTtl, Integer.MAX_VALUE)); + } + /** * Set if domain / host names should be decoded to unicode when received. * See rfc3492. @@ -407,12 +424,14 @@ public DnsNameResolver build() { } DnsCache resolveCache = this.resolveCache != null ? this.resolveCache : newCache(); + DnsCnameCache cnameCache = this.cnameCache != null ? this.cnameCache : newCnameCache(); AuthoritativeDnsServerCache authoritativeDnsServerCache = this.authoritativeDnsServerCache != null ? this.authoritativeDnsServerCache : newAuthoritativeDnsServerCache(); return new DnsNameResolver( eventLoop, channelFactory, resolveCache, + cnameCache, authoritativeDnsServerCache, dnsQueryLifecycleObserverFactory, queryTimeoutMillis, @@ -449,6 +468,9 @@ public DnsNameResolverBuilder copy() { copiedBuilder.resolveCache(resolveCache); } + if (cnameCache != null) { + copiedBuilder.cnameCache(cnameCache); + } if (maxTtl != null && minTtl != null) { copiedBuilder.ttl(minTtl, maxTtl); } diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java index 4f7dfab4208a..39d2259cdc9c 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java @@ -131,6 +131,13 @@ DnsCache resolveCache() { return parent.resolveCache(); } + /** + * The {@link DnsCnameCache} that is used for resolving. + */ + DnsCnameCache cnameCache() { + return parent.cnameCache(); + } + /** * The {@link AuthoritativeDnsServerCache} to use while resolving. */ @@ -237,7 +244,23 @@ void doSearchDomainQuery(String hostname, Promise> nextPromise) { nextContext.internalResolve(hostname, nextPromise); } + private static String hostnameWithDot(String name) { + if (StringUtil.endsWith(name, '.')) { + return name; + } + return name + '.'; + } + private void internalResolve(String name, Promise> promise) { + for (;;) { + // Resolve from cnameCache() until there is no more cname entry cached. + String mapping = cnameCache().get(hostnameWithDot(name)); + if (mapping == null) { + break; + } + name = mapping; + } + DnsServerAddressStream nameServerAddressStream = getNameServers(name); final int end = expectedTypes.length - 1; @@ -448,7 +471,8 @@ private void onResponse(final DnsServerAddressStream nameServerAddrStream, final final DnsRecordType type = question.type(); if (type == DnsRecordType.CNAME) { - onResponseCNAME(question, buildAliasMap(envelope.content()), queryLifecycleObserver, promise); + onResponseCNAME(question, buildAliasMap(envelope.content(), cnameCache(), parent.executor()), + queryLifecycleObserver, promise); return; } @@ -600,7 +624,7 @@ private void onExpectedResponse( // We often get a bunch of CNAMES as well when we asked for A/AAAA. final DnsResponse response = envelope.content(); - final Map cnames = buildAliasMap(response); + final Map cnames = buildAliasMap(response, cnameCache(), parent.executor()); final int answerCount = response.count(DnsSection.ANSWER); boolean found = false; @@ -695,7 +719,7 @@ private void onResponseCNAME( } } - private static Map buildAliasMap(DnsResponse response) { + private static Map buildAliasMap(DnsResponse response, DnsCnameCache cache, EventLoop loop) { final int answerCount = response.count(DnsSection.ANSWER); Map cnames = null; for (int i = 0; i < answerCount; i ++) { @@ -719,7 +743,12 @@ private static Map buildAliasMap(DnsResponse response) { cnames = new HashMap(min(8, answerCount)); } - cnames.put(r.name().toLowerCase(Locale.US), domainName.toLowerCase(Locale.US)); + String name = r.name().toLowerCase(Locale.US); + String mapping = domainName.toLowerCase(Locale.US); + + // Cache the CNAME as well. + cache.cache(hostnameWithDot(name), hostnameWithDot(mapping), r.timeToLive(), loop); + cnames.put(name, mapping); } return cnames != null? cnames : Collections.emptyMap(); @@ -843,6 +872,15 @@ private DnsServerAddressStream getNameServers(String hostname) { private void followCname(DnsQuestion question, String cname, DnsQueryLifecycleObserver queryLifecycleObserver, Promise> promise) { + for (;;) { + // Resolve from cnameCache() until there is no more cname entry cached. + String mapping = cnameCache().get(hostnameWithDot(cname)); + if (mapping == null) { + break; + } + cname = mapping; + } + DnsServerAddressStream stream = getNameServers(cname); final DnsQuestion cnameQuestion; @@ -865,7 +903,7 @@ private boolean query(String hostname, DnsRecordType type, DnsServerAddressStrea // Assume a single failure means that queries will succeed. If the hostname is invalid for one type // there is no case where it is known to be valid for another type. promise.tryFailure(new IllegalArgumentException("Unable to create DNS Question for: [" + hostname + ", " + - type + "]", cause)); + type + ']', cause)); return false; } query(dnsServerAddressStream, 0, question, promise, null); diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsCnameCache.java b/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsCnameCache.java new file mode 100644 index 000000000000..54113c40b258 --- /dev/null +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsCnameCache.java @@ -0,0 +1,47 @@ +/* + * Copyright 2018 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: + * + * http://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.resolver.dns; + +import io.netty.channel.EventLoop; +import io.netty.util.internal.UnstableApi; + +@UnstableApi +public final class NoopDnsCnameCache implements DnsCnameCache { + + public static final NoopDnsCnameCache INSTANCE = new NoopDnsCnameCache(); + + private NoopDnsCnameCache() { } + + @Override + public String get(String hostname) { + return null; + } + + @Override + public void cache(String hostname, String cname, long originalTtl, EventLoop loop) { + // NOOP + } + + @Override + public void clear() { + // NOOP + } + + @Override + public boolean clear(String hostname) { + return false; + } +} diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultDnsCnameCacheTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultDnsCnameCacheTest.java new file mode 100644 index 000000000000..a08702f5f2b1 --- /dev/null +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultDnsCnameCacheTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2018 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: + * + * http://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.resolver.dns; + +import io.netty.channel.DefaultEventLoopGroup; +import io.netty.channel.EventLoop; +import io.netty.channel.EventLoopGroup; +import org.junit.Test; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.*; + +public class DefaultDnsCnameCacheTest { + + @Test + public void testExpire() throws Throwable { + EventLoopGroup group = new DefaultEventLoopGroup(1); + + try { + EventLoop loop = group.next(); + final DefaultDnsCnameCache cache = new DefaultDnsCnameCache(); + cache.cache("netty.io", "mapping.netty.io", 1, loop); + + Throwable error = loop.schedule(new Callable() { + @Override + public Throwable call() { + try { + assertNull(cache.get("netty.io")); + return null; + } catch (Throwable cause) { + return cause; + } + } + }, 1, TimeUnit.SECONDS).get(); + if (error != null) { + throw error; + } + } finally { + group.shutdownGracefully(); + } + } + + @Test + public void testExpireWithDifferentTTLs() { + testExpireWithTTL0(1); + testExpireWithTTL0(1000); + testExpireWithTTL0(1000000); + } + + private static void testExpireWithTTL0(int days) { + EventLoopGroup group = new DefaultEventLoopGroup(1); + + try { + EventLoop loop = group.next(); + final DefaultDnsCnameCache cache = new DefaultDnsCnameCache(); + cache.cache("netty.io", "mapping.netty.io", TimeUnit.DAYS.toSeconds(days), loop); + assertEquals("mapping.netty.io", cache.get("netty.io")); + } finally { + group.shutdownGracefully(); + } + } + + @Test + public void testMultipleCnamesForSameHostname() throws Exception { + EventLoopGroup group = new DefaultEventLoopGroup(1); + + try { + EventLoop loop = group.next(); + final DefaultDnsCnameCache cache = new DefaultDnsCnameCache(); + cache.cache("netty.io", "mapping1.netty.io", 10, loop); + cache.cache("netty.io", "mapping2.netty.io", 10000, loop); + + assertEquals("mapping2.netty.io", cache.get("netty.io")); + } finally { + group.shutdownGracefully(); + } + } + + @Test + public void testAddSameCnameForSameHostname() throws Exception { + EventLoopGroup group = new DefaultEventLoopGroup(1); + + try { + EventLoop loop = group.next(); + final DefaultDnsCnameCache cache = new DefaultDnsCnameCache(); + cache.cache("netty.io", "mapping.netty.io", 10, loop); + cache.cache("netty.io", "mapping.netty.io", 10000, loop); + + assertEquals("mapping.netty.io", cache.get("netty.io")); + } finally { + group.shutdownGracefully(); + } + } + + @Test + public void testClear() throws Exception { + EventLoopGroup group = new DefaultEventLoopGroup(1); + + try { + EventLoop loop = group.next(); + final DefaultDnsCnameCache cache = new DefaultDnsCnameCache(); + cache.cache("x.netty.io", "mapping.netty.io", 100000, loop); + cache.cache("y.netty.io", "mapping.netty.io", 100000, loop); + + assertEquals("mapping.netty.io", cache.get("x.netty.io")); + assertEquals("mapping.netty.io", cache.get("y.netty.io")); + + assertTrue(cache.clear("x.netty.io")); + assertNull(cache.get("x.netty.io")); + assertEquals("mapping.netty.io", cache.get("y.netty.io")); + cache.clear(); + assertNull(cache.get("y.netty.io")); + } finally { + group.shutdownGracefully(); + } + } +} diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java index a3d39b4b978f..2f473626a4ed 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java @@ -82,11 +82,13 @@ import java.util.Map.Entry; import java.util.Queue; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import static io.netty.handler.codec.dns.DnsRecordType.A; @@ -2286,4 +2288,119 @@ public DatagramChannel newChannel() { assertSame(exception, e); } } + + @Test + public void testCNameCached() throws Exception { + final Map cache = new ConcurrentHashMap(); + final AtomicInteger cnameQueries = new AtomicInteger(); + final AtomicInteger aQueries = new AtomicInteger(); + + TestDnsServer dnsServer2 = new TestDnsServer(new RecordStore() { + + @Override + public Set getRecords(QuestionRecord question) { + if ("cname.netty.io".equals(question.getDomainName())) { + aQueries.incrementAndGet(); + + return Collections.singleton(new TestDnsServer.TestResourceRecord( + question.getDomainName(), RecordType.A, + Collections.singletonMap( + DnsAttribute.IP_ADDRESS.toLowerCase(), "10.0.0.99"))); + } + if ("x.netty.io".equals(question.getDomainName())) { + cnameQueries.incrementAndGet(); + + return Collections.singleton(new TestDnsServer.TestResourceRecord( + question.getDomainName(), RecordType.CNAME, + Collections.singletonMap( + DnsAttribute.DOMAIN_NAME.toLowerCase(), "cname.netty.io"))); + } + if ("y.netty.io".equals(question.getDomainName())) { + cnameQueries.incrementAndGet(); + + return Collections.singleton(new TestDnsServer.TestResourceRecord( + question.getDomainName(), RecordType.CNAME, + Collections.singletonMap( + DnsAttribute.DOMAIN_NAME.toLowerCase(), "x.netty.io"))); + } + return Collections.emptySet(); + } + }); + dnsServer2.start(); + DnsNameResolver resolver = null; + try { + DnsNameResolverBuilder builder = newResolver() + .recursionDesired(true) + .resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY) + .maxQueriesPerResolve(16) + .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())) + .resolveCache(NoopDnsCache.INSTANCE) + .cnameCache(new DnsCnameCache() { + @Override + public String get(String hostname) { + assertTrue(hostname, hostname.endsWith(".")); + return cache.get(hostname); + } + + @Override + public void cache(String hostname, String cname, long originalTtl, EventLoop loop) { + assertTrue(hostname, hostname.endsWith(".")); + cache.put(hostname, cname); + } + + @Override + public void clear() { + // NOOP + } + + @Override + public boolean clear(String hostname) { + return false; + } + }); + resolver = builder.build(); + List resolvedAddresses = + resolver.resolveAll("x.netty.io").syncUninterruptibly().getNow(); + assertEquals(1, resolvedAddresses.size()); + assertTrue(resolvedAddresses.contains(InetAddress.getByAddress(new byte[] { 10, 0, 0, 99 }))); + + assertEquals("cname.netty.io.", cache.get("x.netty.io.")); + assertEquals(1, cnameQueries.get()); + assertEquals(1, aQueries.get()); + + resolvedAddresses = + resolver.resolveAll("x.netty.io").syncUninterruptibly().getNow(); + assertEquals(1, resolvedAddresses.size()); + assertTrue(resolvedAddresses.contains(InetAddress.getByAddress(new byte[] { 10, 0, 0, 99 }))); + + // Should not have queried for the CNAME again. + assertEquals(1, cnameQueries.get()); + assertEquals(2, aQueries.get()); + + resolvedAddresses = + resolver.resolveAll("y.netty.io").syncUninterruptibly().getNow(); + assertEquals(1, resolvedAddresses.size()); + assertTrue(resolvedAddresses.contains(InetAddress.getByAddress(new byte[] { 10, 0, 0, 99 }))); + + assertEquals("x.netty.io.", cache.get("y.netty.io.")); + + // Will only query for one CNAME + assertEquals(2, cnameQueries.get()); + assertEquals(3, aQueries.get()); + + resolvedAddresses = + resolver.resolveAll("y.netty.io").syncUninterruptibly().getNow(); + assertEquals(1, resolvedAddresses.size()); + assertTrue(resolvedAddresses.contains(InetAddress.getByAddress(new byte[] { 10, 0, 0, 99 }))); + + // Should not have queried for the CNAME again. + assertEquals(2, cnameQueries.get()); + assertEquals(4, aQueries.get()); + } finally { + dnsServer2.stop(); + if (resolver != null) { + resolver.close(); + } + } + } } From b81c8ed55c00c068a991c68b1ca222b6886ceaa0 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 27 Sep 2018 19:45:58 +0200 Subject: [PATCH 188/417] Use AuthoritativeDnsServerCache for creating the new redirect stream. (#8316) * Use AuthoritativeDnsServerCache for creating the new redirect stream. Motivation: At the moment if a user wants to provide custom sorting of the nameservers used for redirects it needs to be implemented in two places. This is more complicated as it needs to be. Modifications: - Just delegate to the AuthoritativeDnsServerCache always as we fill it before we call newRedirectDnsServerStream anyway. Result: Easier way for the user to implement custom sorting. --- .../java/io/netty/resolver/dns/DnsNameResolver.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java index 069c31c66dc3..6206c28ac663 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java @@ -450,8 +450,14 @@ final DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory() { */ protected DnsServerAddressStream newRedirectDnsServerStream( @SuppressWarnings("unused") String hostname, List nameservers) { - Collections.sort(nameservers, nameServerComparator); - return new SequentialDnsServerAddressStream(nameservers, 0); + DnsServerAddressStream cached = authoritativeDnsServerCache().get(hostname); + if (cached == null || cached.size() == 0) { + // If there is no cache hit (which may be the case for example when a NoopAuthoritativeDnsServerCache + // is used), we will just directly use the provided nameservers. + Collections.sort(nameservers, nameServerComparator); + return new SequentialDnsServerAddressStream(nameservers, 0); + } + return cached; } /** From 3a9ac829d53e1da7365db2d8f544d7d112147321 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 27 Sep 2018 18:56:12 +0000 Subject: [PATCH 189/417] [maven-release-plugin] prepare release netty-4.1.30.Final --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 4 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 77 insertions(+), 73 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 73d7d1de81a3..8da690a19f04 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 2d085a50de22..985b28c55fa6 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.30.Final-SNAPSHOT + 4.1.30.Final pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.30.Final @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec-dns - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec-haproxy - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec-http - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec-http2 - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec-memcache - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec-mqtt - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec-redis - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec-smtp - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec-socks - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec-stomp - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-codec-xml - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-common - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-dev-tools - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-handler - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-handler-proxy - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-resolver - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-resolver-dns - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-transport - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-transport-rxtx - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-transport-sctp - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-transport-udt - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-example - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-all - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-transport-native-unix-common - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-transport-native-unix-common - 4.1.30.Final-SNAPSHOT + 4.1.30.Final linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.30.Final-SNAPSHOT + 4.1.30.Final osx-x86_64 io.netty netty-transport-native-epoll - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-transport-native-epoll - 4.1.30.Final-SNAPSHOT + 4.1.30.Final linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.30.Final-SNAPSHOT + 4.1.30.Final io.netty netty-transport-native-kqueue - 4.1.30.Final-SNAPSHOT + 4.1.30.Final osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index dff323d8b486..9ac82f2c03eb 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 49aaecdadb32..e8c1b6994562 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 2a9ca8e8f696..02e7a0c4e68f 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 2a519d1ed089..903081e73750 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index beda9f717172..2cee7d64c61d 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 067f35db3d85..c3f1fdbea556 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 6b27ad7d5fe5..845883ccbdbd 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 5e5f883c72fa..4c748d605669 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index 23dc22904c3a..6f0e2fb0c409 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 31fcfbdf4083..313f39edbcbc 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 67f5d804ef4f..ed68ebc0076b 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index 54bbf043bbce..9c2f1e587e00 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 2799b130995b..86ced2fee6ce 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-codec diff --git a/common/pom.xml b/common/pom.xml index e2022967a8a9..e1955bf32b60 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 34084e5d1559..3f003d01f389 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.30.Final-SNAPSHOT + 4.1.30.Final Netty/Dev-Tools @@ -50,4 +50,8 @@ + + + netty-4.1.30.Final + diff --git a/example/pom.xml b/example/pom.xml index f9d0a6be83cf..c8838701dd36 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 154fac180273..47e7cf77d6ee 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index ebfbf773c417..f1bd5e870b3e 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index ba1c5f6f3bb2..c34665c8d326 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-microbench @@ -34,7 +34,7 @@ 1.21 - + diff --git a/pom.xml b/pom.xml index 3670a6fb2db3..2511846fbe1e 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.30.Final-SNAPSHOT + 4.1.30.Final Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.30.Final diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index cbcecd3fdea3..dd25d49ae4ec 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index 583135a0f6da..0974d9f9bab4 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 9fdb78e9ceb0..25dce2e0a62f 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 430f4d10c521..43abe35d6104 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index de6c30fdb334..1b38f4bf5c6b 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 0648979e1317..e433664ce94f 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index b125492b9439..cdd276315846 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 8a0dd0d699a7..3ad57f50d197 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 01350b23b24f..7cc02a1f787c 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 646f54dc5e70..4cba2c5e7e27 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index b27aae873a78..40bc0a995f13 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 3ac755f2c643..5a2c22b28528 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index 9beb7bbe301d..2237045eb58b 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 1d0c3881f284..5c8ec49cf96a 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 10707eee04f1..51f3e0c78a91 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 09b47e6d6e63..c69e2b2d84c7 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final-SNAPSHOT + 4.1.30.Final netty-transport From 2d7cb47eddc79c4b43a1fb3996e8cb8bde15bff3 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 27 Sep 2018 19:00:45 +0000 Subject: [PATCH 190/417] [maven-release-plugin] prepare for next development iteration --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 72 insertions(+), 76 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 8da690a19f04..64b3fe8cbc9c 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 985b28c55fa6..b20066b07449 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.30.Final + 4.1.31.Final-SNAPSHOT pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.30.Final + HEAD @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec-dns - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec-haproxy - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec-http - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec-http2 - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec-memcache - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec-mqtt - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec-redis - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec-smtp - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec-socks - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec-stomp - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-codec-xml - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-common - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-dev-tools - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-handler - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-handler-proxy - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-resolver - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-resolver-dns - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-transport - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-transport-rxtx - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-transport-sctp - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-transport-udt - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-example - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-all - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.30.Final + 4.1.31.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.30.Final + 4.1.31.Final-SNAPSHOT osx-x86_64 io.netty netty-transport-native-epoll - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-transport-native-epoll - 4.1.30.Final + 4.1.31.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.30.Final + 4.1.31.Final-SNAPSHOT io.netty netty-transport-native-kqueue - 4.1.30.Final + 4.1.31.Final-SNAPSHOT osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 9ac82f2c03eb..f45cca7af2eb 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index e8c1b6994562..482711865f01 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 02e7a0c4e68f..4eca96d20b6f 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 903081e73750..342c5cf613e0 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 2cee7d64c61d..f02f3159cd6f 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index c3f1fdbea556..ff45bf73335f 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 845883ccbdbd..1b053b17c407 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 4c748d605669..2c886ba30f69 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index 6f0e2fb0c409..9273d5f19d5a 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 313f39edbcbc..1996037296dd 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index ed68ebc0076b..a805e15ab92d 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index 9c2f1e587e00..21fa7c0ce440 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 86ced2fee6ce..c44fe84cd4fe 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-codec diff --git a/common/pom.xml b/common/pom.xml index e1955bf32b60..6add1d470ad8 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 3f003d01f389..c7f1cc9b67b5 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.30.Final + 4.1.31.Final-SNAPSHOT Netty/Dev-Tools @@ -50,8 +50,4 @@ - - - netty-4.1.30.Final - diff --git a/example/pom.xml b/example/pom.xml index c8838701dd36..567e4f041f94 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 47e7cf77d6ee..26e3215501d8 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index f1bd5e870b3e..daf4dceb5826 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index c34665c8d326..67e656aa6f67 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-microbench diff --git a/pom.xml b/pom.xml index 2511846fbe1e..0aa5dd28ad36 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.30.Final + 4.1.31.Final-SNAPSHOT Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.30.Final + HEAD diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index dd25d49ae4ec..54fca875289a 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index 0974d9f9bab4..249bf7088a13 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 25dce2e0a62f..1b960e67031c 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 43abe35d6104..23e0a82b9443 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 1b38f4bf5c6b..1dc4edd02f42 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index e433664ce94f..c91f06ac4c51 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index cdd276315846..a19adabeacda 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 3ad57f50d197..5ffb1923b79d 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 7cc02a1f787c..5334f686d09c 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 4cba2c5e7e27..3e3ca9e23df0 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 40bc0a995f13..3462aabcf7dc 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 5a2c22b28528..3adad37a371a 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index 2237045eb58b..ff600b3cfa88 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 5c8ec49cf96a..5e5c29819188 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 51f3e0c78a91..4160bc6ea24b 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index c69e2b2d84c7..701ade2fcea0 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.30.Final + 4.1.31.Final-SNAPSHOT netty-transport From a208f6dc7c775ce8d7934f252cd6fa7527643f76 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 28 Sep 2018 09:19:58 +0200 Subject: [PATCH 191/417] Do the same extended checks as the JDK when a X509TrustManager is used with the OpenSSL provider. (#8307) Motivation: When a X509TrustManager is used while configure the SslContext the JDK automatically does some extra checks during validation of provided certs by the remote peer. We should do the same when our native implementation is used. Modification: - Automatically wrap a X509TrustManager and so do the same validations as the JDK does. - Add unit tests. Result: More consistent behaviour. Fixes https://github.com/netty/netty/issues/6664. --- .../util/internal/PlatformDependent.java | 4 + .../ssl/OpenSslX509TrustManagerWrapper.java | 190 ++++++++++++++++++ .../ssl/ReferenceCountedOpenSslContext.java | 2 +- .../io/netty/handler/ssl/SSLEngineTest.java | 78 ++++++- 4 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslX509TrustManagerWrapper.java diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index fa38e8921d91..ee2101cbbeaf 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -404,6 +404,10 @@ public static ByteBuffer directBuffer(long memoryAddress, int size) { "sun.misc.Unsafe or java.nio.DirectByteBuffer.(long, int) not available"); } + public static Object getObject(Object object, long fieldOffset) { + return PlatformDependent0.getObject(object, fieldOffset); + } + public static int getInt(Object object, long fieldOffset) { return PlatformDependent0.getInt(object, fieldOffset); } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509TrustManagerWrapper.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509TrustManagerWrapper.java new file mode 100644 index 000000000000..0a0db0bbf4ea --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509TrustManagerWrapper.java @@ -0,0 +1,190 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.util.internal.EmptyArrays; +import io.netty.util.internal.PlatformDependent; +import io.netty.util.internal.logging.InternalLogger; +import io.netty.util.internal.logging.InternalLoggerFactory; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509ExtendedTrustManager; +import javax.net.ssl.X509TrustManager; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivilegedAction; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * Utility which allows to wrap {@link X509TrustManager} implementations with the internal implementation used by + * {@code SSLContextImpl} that provides extended verification. + * + * This is really a "hack" until there is an official API as requested on the in + * JDK-8210843. + */ +final class OpenSslX509TrustManagerWrapper { + private static final InternalLogger LOGGER = InternalLoggerFactory + .getInstance(OpenSslX509TrustManagerWrapper.class); + private static final TrustManagerWrapper WRAPPER; + + static { + // By default we will not do any wrapping but just return the passed in manager. + TrustManagerWrapper wrapper = new TrustManagerWrapper() { + @Override + public X509TrustManager wrapIfNeeded(X509TrustManager manager) { + return manager; + } + }; + + Throwable cause = null; + Throwable unsafeCause = PlatformDependent.getUnsafeUnavailabilityCause(); + if (unsafeCause == null) { + SSLContext context; + try { + context = newSSLContext(); + // Now init with an array that only holds a X509TrustManager. This should be wrapped into an + // AbstractTrustManagerWrapper which will delegate the TrustManager itself but also do extra + // validations. + // + // See: + // - http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/ + // cadea780bc76/src/share/classes/sun/security/ssl/SSLContextImpl.java#l127 + context.init(null, new TrustManager[] { + new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) + throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) + throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return EmptyArrays.EMPTY_X509_CERTIFICATES; + } + } + }, null); + } catch (Throwable error) { + context = null; + cause = error; + } + if (cause != null) { + LOGGER.debug("Unable to access wrapped TrustManager", cause); + } else { + final SSLContext finalContext = context; + Object maybeWrapper = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + try { + Field contextSpiField = SSLContext.class.getDeclaredField("contextSpi"); + final long spiOffset = PlatformDependent.objectFieldOffset(contextSpiField); + Object spi = PlatformDependent.getObject(finalContext, spiOffset); + if (spi != null) { + Class clazz = spi.getClass(); + + // Let's cycle through the whole hierarchy until we find what we are looking for or + // there is nothing left in which case we will not wrap at all. + do { + try { + Field trustManagerField = clazz.getDeclaredField("trustManager"); + final long tmOffset = PlatformDependent.objectFieldOffset(trustManagerField); + Object trustManager = PlatformDependent.getObject(spi, tmOffset); + if (trustManager instanceof X509ExtendedTrustManager) { + return new UnsafeTrustManagerWrapper(spiOffset, tmOffset); + } + } catch (NoSuchFieldException ignore) { + // try next + } + clazz = clazz.getSuperclass(); + } while (clazz != null); + } + throw new NoSuchFieldException(); + } catch (NoSuchFieldException e) { + return e; + } catch (SecurityException e) { + return e; + } + } + }); + if (maybeWrapper instanceof Throwable) { + LOGGER.debug("Unable to access wrapped TrustManager", (Throwable) maybeWrapper); + } else { + wrapper = (TrustManagerWrapper) maybeWrapper; + } + } + } else { + LOGGER.debug("Unable to access wrapped TrustManager", cause); + } + WRAPPER = wrapper; + } + + private OpenSslX509TrustManagerWrapper() { } + + static X509TrustManager wrapIfNeeded(X509TrustManager trustManager) { + return WRAPPER.wrapIfNeeded(trustManager); + } + + private interface TrustManagerWrapper { + X509TrustManager wrapIfNeeded(X509TrustManager manager); + } + + private static SSLContext newSSLContext() throws NoSuchAlgorithmException { + return SSLContext.getInstance("TLS"); + } + + private static final class UnsafeTrustManagerWrapper implements TrustManagerWrapper { + private final long spiOffset; + private final long tmOffset; + + UnsafeTrustManagerWrapper(long spiOffset, long tmOffset) { + this.spiOffset = spiOffset; + this.tmOffset = tmOffset; + } + + @Override + public X509TrustManager wrapIfNeeded(X509TrustManager manager) { + if (!(manager instanceof X509ExtendedTrustManager)) { + try { + SSLContext ctx = newSSLContext(); + ctx.init(null, new TrustManager[] { manager }, null); + Object spi = PlatformDependent.getObject(ctx, spiOffset); + if (spi != null) { + Object tm = PlatformDependent.getObject(spi, tmOffset); + if (tm instanceof X509ExtendedTrustManager) { + return (X509TrustManager) tm; + } + } + } catch (NoSuchAlgorithmException e) { + // This should never happen as we did the same in the static + // before. + PlatformDependent.throwException(e); + } catch (KeyManagementException e) { + // This should never happen as we did the same in the static + // before. + PlatformDependent.throwException(e); + } + } + return manager; + } + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index 18c86795744d..3a5023b98e45 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -508,7 +508,7 @@ protected static X509Certificate[] certificates(byte[][] chain) { protected static X509TrustManager chooseTrustManager(TrustManager[] managers) { for (TrustManager m : managers) { if (m instanceof X509TrustManager) { - return (X509TrustManager) m; + return OpenSslX509TrustManagerWrapper.wrapIfNeeded((X509TrustManager) m); } } throw new IllegalStateException("no X509TrustManager found"); diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index c35108875406..efb3b7f58acc 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -35,6 +35,7 @@ import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; +import io.netty.handler.ssl.util.SimpleTrustManagerFactory; import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; import io.netty.util.ReferenceCountUtil; @@ -58,7 +59,9 @@ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; +import java.security.InvalidAlgorithmParameterException; import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.Provider; import java.security.cert.Certificate; import java.security.cert.CertificateException; @@ -71,6 +74,7 @@ import java.util.concurrent.TimeUnit; import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.SNIHostName; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; @@ -78,6 +82,10 @@ import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.TrustManagerFactorySpi; +import javax.net.ssl.X509TrustManager; import javax.security.cert.X509Certificate; import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2; @@ -2391,7 +2399,7 @@ private void testDisableProtocols(String protocol, String... disabledProtocols) Set supported = new HashSet(Arrays.asList(server.getSupportedProtocols())); if (supported.contains(protocol)) { server.setEnabledProtocols(server.getSupportedProtocols()); - Assert.assertEquals(supported, new HashSet(Arrays.asList(server.getSupportedProtocols()))); + assertEquals(supported, new HashSet(Arrays.asList(server.getSupportedProtocols()))); for (String disabled : disabledProtocols) { supported.remove(disabled); @@ -2401,7 +2409,7 @@ private void testDisableProtocols(String protocol, String... disabledProtocols) return; } server.setEnabledProtocols(supported.toArray(new String[0])); - Assert.assertEquals(supported, new HashSet(Arrays.asList(server.getEnabledProtocols()))); + assertEquals(supported, new HashSet(Arrays.asList(server.getEnabledProtocols()))); server.setEnabledProtocols(server.getSupportedProtocols()); } } finally { @@ -2411,6 +2419,72 @@ private void testDisableProtocols(String protocol, String... disabledProtocols) } } + @Test + public void testUsingX509TrustManagerVerifiesHostname() throws Exception { + SelfSignedCertificate cert = new SelfSignedCertificate(); + clientSslCtx = SslContextBuilder + .forClient() + .trustManager(new TrustManagerFactory(new TrustManagerFactorySpi() { + @Override + protected void engineInit(KeyStore keyStore) { + // NOOP + } + @Override + protected TrustManager[] engineGetTrustManagers() { + // Provide a custom trust manager, this manager trust all certificates + return new TrustManager[] { + new X509TrustManager() { + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] x509Certificates, String s) { + // NOOP + } + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] x509Certificates, String s) { + // NOOP + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return EmptyArrays.EMPTY_X509_CERTIFICATES; + } + } + }; + } + + @Override + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { + } + }, null, TrustManagerFactory.getDefaultAlgorithm()) { + }) + .sslProvider(sslClientProvider()) + .build(); + + SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT, "netty.io", 1234)); + SSLParameters sslParameters = client.getSSLParameters(); + sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); + client.setSSLParameters(sslParameters); + + serverSslCtx = SslContextBuilder + .forServer(cert.certificate(), cert.privateKey()) + .sslProvider(sslServerProvider()) + .build(); + + SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + try { + handshake(client, server); + fail(); + } catch (SSLException expected) { + // expected as the hostname not matches. + } finally { + cleanupClientSslEngine(client); + cleanupServerSslEngine(server); + cert.delete(); + } + } + protected SSLEngine wrapEngine(SSLEngine engine) { return engine; } From 73acac13f48be32e04f936f3d09e202e7590f0a0 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 28 Sep 2018 10:54:05 +0200 Subject: [PATCH 192/417] Check if hostname validation is supported before trying to use in test. (#8333) Motivation: a208f6dc7c775ce8d7934f252cd6fa7527643f76 added a testcase which uses hostname validation which may not be supported by OpenSSL depending on the version that is used. We should check first before we try to use it. Modifications: Add assumeTrue(...) check to ensure hostname validation is supported before trying to run the test. Result: No more test-failures on OpenSSL versions < 1.0.2. --- .../src/test/java/io/netty/handler/ssl/SSLEngineTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index efb3b7f58acc..4248e1fca48d 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -46,6 +46,7 @@ import io.netty.util.internal.StringUtil; import org.junit.After; import org.junit.Assert; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -2421,6 +2422,12 @@ private void testDisableProtocols(String protocol, String... disabledProtocols) @Test public void testUsingX509TrustManagerVerifiesHostname() throws Exception { + SslProvider clientProvider = sslClientProvider(); + if (clientProvider == SslProvider.OPENSSL || clientProvider == SslProvider.OPENSSL_REFCNT) { + // Need to check if we support hostname validation in the current used OpenSSL version before running + // the test. + Assume.assumeTrue(OpenSsl.supportsHostnameValidation()); + } SelfSignedCertificate cert = new SelfSignedCertificate(); clientSslCtx = SslContextBuilder .forClient() From 59973e93dd7da715eee709788573e3515cc50238 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 28 Sep 2018 11:34:38 +0200 Subject: [PATCH 193/417] Ensure X509KeyManager methods are called on the correct time when using server-side and support more methods of ExtendedSSLSession. (#8283) Motivation: Before when on server-side we just called the X509KeyManager methods when handshake() was called the first time which is not quite correct as we may not have received the full SSL hello / handshake and so could not extra for example the SNI hostname that was requested. OpenSSL exposes the SSL_CTX_set_cert_cb function which allows to set a callback which is executed at the correct moment, so we should use it. This also allows us to support more methods of ExtendedSSLSession easily. Modifications: - Make use of new methods exposed by netty-tcnative since https://github.com/netty/netty-tcnative/pull/388 to ensure we select the key material at the correct time. - Implement more methods of ExtendedOpenSslSession - Add unit tests to ensure we are able to retrieve various things on server-side in the X509KeyManager and so verify it is called at the correct time. - Simplify code by using new netty-tcnative methods. Result: More correct implementation for server-side usage and more complete implemented of ExtendedSSLSession. --- .../handler/ssl/ExtendedOpenSslSession.java | 56 +++++++--------- .../io/netty/handler/ssl/Java8SslUtils.java | 7 ++ .../handler/ssl/OpenSslClientContext.java | 5 -- .../ssl/OpenSslKeyMaterialManager.java | 28 +++----- .../handler/ssl/OpenSslServerContext.java | 13 +--- .../ReferenceCountedOpenSslClientContext.java | 32 +++++---- .../ssl/ReferenceCountedOpenSslContext.java | 6 +- .../ssl/ReferenceCountedOpenSslEngine.java | 66 +++++++++++++++--- .../ReferenceCountedOpenSslServerContext.java | 67 ++++++++++++------- .../ssl/SignatureAlgorithmConverter.java | 61 +++++++++++++++++ .../netty/handler/ssl/OpenSslTestUtils.java | 4 ++ .../ssl/SignatureAlgorithmConverterTest.java | 44 ++++++++++++ .../handler/ssl/SniClientJava8TestUtil.java | 22 ++++-- .../io/netty/handler/ssl/SniClientTest.java | 3 +- .../io/netty/handler/ssl/SslErrorTest.java | 4 +- pom.xml | 2 +- 16 files changed, 286 insertions(+), 134 deletions(-) create mode 100644 handler/src/main/java/io/netty/handler/ssl/SignatureAlgorithmConverter.java create mode 100644 handler/src/test/java/io/netty/handler/ssl/SignatureAlgorithmConverterTest.java diff --git a/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java b/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java index 87185db529d3..69a6125d172a 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java +++ b/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java @@ -15,8 +15,6 @@ */ package io.netty.handler.ssl; -import io.netty.util.internal.EmptyArrays; - import javax.net.ssl.ExtendedSSLSession; import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; @@ -60,128 +58,122 @@ public List getStatusResponses() { } @Override - public void handshakeFinished() throws SSLException { + public final void handshakeFinished() throws SSLException { wrapped.handshakeFinished(); } @Override - public void tryExpandApplicationBufferSize(int packetLengthDataOnly) { + public final void tryExpandApplicationBufferSize(int packetLengthDataOnly) { wrapped.tryExpandApplicationBufferSize(packetLengthDataOnly); } @Override - public String[] getLocalSupportedSignatureAlgorithms() { + public final String[] getLocalSupportedSignatureAlgorithms() { return LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS.clone(); } @Override - public String[] getPeerSupportedSignatureAlgorithms() { - // Always return empty for now. - return EmptyArrays.EMPTY_STRINGS; - } - - @Override - public byte[] getId() { + public final byte[] getId() { return wrapped.getId(); } @Override - public SSLSessionContext getSessionContext() { + public final SSLSessionContext getSessionContext() { return wrapped.getSessionContext(); } @Override - public long getCreationTime() { + public final long getCreationTime() { return wrapped.getCreationTime(); } @Override - public long getLastAccessedTime() { + public final long getLastAccessedTime() { return wrapped.getLastAccessedTime(); } @Override - public void invalidate() { + public final void invalidate() { wrapped.invalidate(); } @Override - public boolean isValid() { + public final boolean isValid() { return wrapped.isValid(); } @Override - public void putValue(String s, Object o) { + public final void putValue(String s, Object o) { wrapped.putValue(s, o); } @Override - public Object getValue(String s) { + public final Object getValue(String s) { return wrapped.getValue(s); } @Override - public void removeValue(String s) { + public final void removeValue(String s) { wrapped.removeValue(s); } @Override - public String[] getValueNames() { + public final String[] getValueNames() { return wrapped.getValueNames(); } @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + public final Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { return wrapped.getPeerCertificates(); } @Override - public Certificate[] getLocalCertificates() { + public final Certificate[] getLocalCertificates() { return wrapped.getLocalCertificates(); } @Override - public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { + public final X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { return wrapped.getPeerCertificateChain(); } @Override - public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { + public final Principal getPeerPrincipal() throws SSLPeerUnverifiedException { return wrapped.getPeerPrincipal(); } @Override - public Principal getLocalPrincipal() { + public final Principal getLocalPrincipal() { return wrapped.getLocalPrincipal(); } @Override - public String getCipherSuite() { + public final String getCipherSuite() { return wrapped.getCipherSuite(); } @Override - public String getProtocol() { + public final String getProtocol() { return wrapped.getProtocol(); } @Override - public String getPeerHost() { + public final String getPeerHost() { return wrapped.getPeerHost(); } @Override - public int getPeerPort() { + public final int getPeerPort() { return wrapped.getPeerPort(); } @Override - public int getPacketBufferSize() { + public final int getPacketBufferSize() { return wrapped.getPacketBufferSize(); } @Override - public int getApplicationBufferSize() { + public final int getApplicationBufferSize() { return wrapped.getApplicationBufferSize(); } } diff --git a/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java b/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java index 648c0f2e65c6..c47d96571218 100644 --- a/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java +++ b/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java @@ -62,6 +62,13 @@ static List getSniHostNames(List names) { return sniServerNames; } + static List getSniHostName(byte[] hostname) { + if (hostname == null || hostname.length == 0) { + return Collections.emptyList(); + } + return Collections.singletonList(new SNIHostName(hostname)); + } + static boolean getUseCipherSuitesOrder(SSLParameters sslParameters) { return sslParameters.getUseCipherSuitesOrder(); } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java index 46412e9f526b..6856c7f2746c 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java @@ -203,9 +203,4 @@ public OpenSslClientContext(File trustCertCollectionFile, TrustManagerFactory tr public OpenSslSessionContext sessionContext() { return sessionContext; } - - @Override - OpenSslKeyMaterialManager keyMaterialManager() { - return null; - } } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java index f365dee279c5..8b8a41d1146b 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java @@ -74,36 +74,25 @@ void setKeyMaterialServerSide(ReferenceCountedOpenSslEngine engine) throws SSLEx if (type != null) { String alias = chooseServerAlias(engine, type); if (alias != null && aliases.add(alias)) { - OpenSslKeyMaterial keyMaterial = null; - try { - keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias); - if (keyMaterial != null) { - SSL.setKeyMaterialServerSide( - ssl, keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress()); - } - } catch (SSLException e) { - throw e; - } catch (Exception e) { - throw new SSLException(e); - } finally { - if (keyMaterial != null) { - keyMaterial.release(); - } - } + setKeyMaterial(engine, alias); } } } } - void setKeyMaterialClientSide(ReferenceCountedOpenSslEngine engine, long certOut, long keyOut, String[] keyTypes, + void setKeyMaterialClientSide(ReferenceCountedOpenSslEngine engine, String[] keyTypes, X500Principal[] issuer) throws SSLException { String alias = chooseClientAlias(engine, keyTypes, issuer); + setKeyMaterial(engine, alias); + } + + private void setKeyMaterial(ReferenceCountedOpenSslEngine engine, String alias) throws SSLException { OpenSslKeyMaterial keyMaterial = null; try { keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias); if (keyMaterial != null) { - SSL.setKeyMaterialClientSide(engine.sslPointer(), certOut, keyOut, - keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress()); + SSL.setKeyMaterial(engine.sslPointer(), + keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress()); } } catch (SSLException e) { throw e; @@ -115,7 +104,6 @@ void setKeyMaterialClientSide(ReferenceCountedOpenSslEngine engine, long certOut } } } - private String chooseClientAlias(ReferenceCountedOpenSslEngine engine, String[] keyTypes, X500Principal[] issuer) { X509KeyManager manager = provider.keyManager(); diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java index f57434b133c0..e27b05ae8b9c 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java @@ -15,7 +15,6 @@ */ package io.netty.handler.ssl; -import io.netty.handler.ssl.ReferenceCountedOpenSslServerContext.ServerContext; import io.netty.internal.tcnative.SSL; import java.io.File; @@ -37,7 +36,6 @@ */ public final class OpenSslServerContext extends OpenSslContext { private final OpenSslServerSessionContext sessionContext; - private final OpenSslKeyMaterialManager keyMaterialManager; /** * Creates a new instance. @@ -349,10 +347,8 @@ private OpenSslServerContext( // Create a new SSL_CTX and configure it. boolean success = false; try { - ServerContext context = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, - keyCertChain, key, keyPassword, keyManagerFactory); - sessionContext = context.sessionContext; - keyMaterialManager = context.keyMaterialManager; + sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, + keyCertChain, key, keyPassword, keyManagerFactory); success = true; } finally { if (!success) { @@ -365,9 +361,4 @@ private OpenSslServerContext( public OpenSslServerSessionContext sessionContext() { return sessionContext; } - - @Override - OpenSslKeyMaterialManager keyMaterialManager() { - return keyMaterialManager; - } } diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java index 29815c429e99..9e524a7a0083 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java @@ -15,15 +15,16 @@ */ package io.netty.handler.ssl; +import io.netty.internal.tcnative.CertificateCallback; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; -import io.netty.internal.tcnative.CertificateRequestedCallback; import io.netty.internal.tcnative.SSL; import io.netty.internal.tcnative.SSLContext; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -68,11 +69,6 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted } } - @Override - OpenSslKeyMaterialManager keyMaterialManager() { - return null; - } - @Override public OpenSslSessionContext sessionContext() { return sessionContext; @@ -118,7 +114,7 @@ static OpenSslSessionContext newSessionContext(ReferenceCountedOpenSslContext th if (keyMaterialProvider != null) { OpenSslKeyMaterialManager materialManager = new OpenSslKeyMaterialManager(keyMaterialProvider); - SSLContext.setCertRequestedCallback(ctx, new OpenSslCertificateRequestedCallback( + SSLContext.setCertificateCallback(ctx, new OpenSslClientCertificateCallback( engineMap, materialManager)); } } @@ -238,18 +234,17 @@ void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, S } } - private static final class OpenSslCertificateRequestedCallback implements CertificateRequestedCallback { + private static final class OpenSslClientCertificateCallback implements CertificateCallback { private final OpenSslEngineMap engineMap; private final OpenSslKeyMaterialManager keyManagerHolder; - OpenSslCertificateRequestedCallback(OpenSslEngineMap engineMap, OpenSslKeyMaterialManager keyManagerHolder) { + OpenSslClientCertificateCallback(OpenSslEngineMap engineMap, OpenSslKeyMaterialManager keyManagerHolder) { this.engineMap = engineMap; this.keyManagerHolder = keyManagerHolder; } @Override - public void requested( - long ssl, long certOut, long keyOut, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) { + public void handle(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) throws Exception { final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); try { final Set keyTypesSet = supportedClientKeyTypes(keyTypeBytes); @@ -263,7 +258,7 @@ public void requested( issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]); } } - keyManagerHolder.setKeyMaterialClientSide(engine, certOut, keyOut, keyTypes, issuers); + keyManagerHolder.setKeyMaterialClientSide(engine, keyTypes, issuers); } catch (Throwable cause) { logger.debug("request of key failed", cause); SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem"); @@ -281,6 +276,9 @@ public void requested( * {@code X509ExtendedKeyManager.chooseEngineClientAlias}. */ private static Set supportedClientKeyTypes(byte[] clientCertificateTypes) { + if (clientCertificateTypes == null) { + return Collections.emptySet(); + } Set result = new HashSet(clientCertificateTypes.length); for (byte keyTypeCode : clientCertificateTypes) { String keyType = clientKeyType(keyTypeCode); @@ -296,15 +294,15 @@ private static Set supportedClientKeyTypes(byte[] clientCertificateTypes private static String clientKeyType(byte clientCertificateType) { // See also http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml switch (clientCertificateType) { - case CertificateRequestedCallback.TLS_CT_RSA_SIGN: + case CertificateCallback.TLS_CT_RSA_SIGN: return OpenSslKeyMaterialManager.KEY_TYPE_RSA; // RFC rsa_sign - case CertificateRequestedCallback.TLS_CT_RSA_FIXED_DH: + case CertificateCallback.TLS_CT_RSA_FIXED_DH: return OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA; // RFC rsa_fixed_dh - case CertificateRequestedCallback.TLS_CT_ECDSA_SIGN: + case CertificateCallback.TLS_CT_ECDSA_SIGN: return OpenSslKeyMaterialManager.KEY_TYPE_EC; // RFC ecdsa_sign - case CertificateRequestedCallback.TLS_CT_RSA_FIXED_ECDH: + case CertificateCallback.TLS_CT_RSA_FIXED_ECDH: return OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA; // RFC rsa_fixed_ecdh - case CertificateRequestedCallback.TLS_CT_ECDSA_FIXED_ECDH: + case CertificateCallback.TLS_CT_ECDSA_FIXED_ECDH: return OpenSslKeyMaterialManager.KEY_TYPE_EC_EC; // RFC ecdsa_fixed_ecdh default: return null; diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index 3a5023b98e45..714f473f7a7f 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -225,7 +225,9 @@ public String run() { boolean success = false; try { try { - ctx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, mode); + int opts = SSL.SSL_PROTOCOL_SSLV3 | SSL.SSL_PROTOCOL_TLSV1 | + SSL.SSL_PROTOCOL_TLSV1_1 | SSL.SSL_PROTOCOL_TLSV1_2; + ctx = SSLContext.make(opts, mode); } catch (Exception e) { throw new SSLException("failed to create an SSL_CTX", e); } @@ -366,8 +368,6 @@ SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort, bool return new ReferenceCountedOpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode, true); } - abstract OpenSslKeyMaterialManager keyMaterialManager(); - /** * Returns a new server-side {@link SSLEngine} with the current configuration. */ diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 6058bf16a7ab..49851c93b5a3 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -20,6 +20,7 @@ import io.netty.internal.tcnative.Buffer; import io.netty.internal.tcnative.SSL; import io.netty.util.AbstractReferenceCounted; +import io.netty.util.CharsetUtil; import io.netty.util.ReferenceCounted; import io.netty.util.ResourceLeakDetector; import io.netty.util.ResourceLeakDetectorFactory; @@ -142,7 +143,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc // OpenSSL state private long ssl; private long networkBIO; - private boolean certificateSet; private enum HandshakeState { /** @@ -217,7 +217,6 @@ protected void deallocate() { private final Certificate[] localCerts; private final ByteBuffer[] singleSrcBuffer = new ByteBuffer[1]; private final ByteBuffer[] singleDstBuffer = new ByteBuffer[1]; - private final OpenSslKeyMaterialManager keyMaterialManager; private final boolean enableOcsp; private int maxWrapOverhead; private int maxWrapBufferSize; @@ -238,18 +237,69 @@ protected void deallocate() { * wrap or unwrap call. * @param leakDetection {@code true} to enable leak detection of this object. */ - ReferenceCountedOpenSslEngine(ReferenceCountedOpenSslContext context, ByteBufAllocator alloc, String peerHost, + ReferenceCountedOpenSslEngine(ReferenceCountedOpenSslContext context, final ByteBufAllocator alloc, String peerHost, int peerPort, boolean jdkCompatibilityMode, boolean leakDetection) { super(peerHost, peerPort); OpenSsl.ensureAvailability(); this.alloc = checkNotNull(alloc, "alloc"); apn = (OpenSslApplicationProtocolNegotiator) context.applicationProtocolNegotiator(); clientMode = context.isClient(); - if (PlatformDependent.javaVersion() >= 7 && context.isClient()) { + if (PlatformDependent.javaVersion() >= 7) { session = new ExtendedOpenSslSession(new DefaultOpenSslSession(context.sessionContext())) { + private String[] peerSupportedSignatureAlgorithms; + private List requestedServerNames; + @Override public List getRequestedServerNames() { - return Java8SslUtils.getSniHostNames(sniHostNames); + if (clientMode) { + return Java8SslUtils.getSniHostNames(sniHostNames); + } else { + synchronized (ReferenceCountedOpenSslEngine.this) { + if (requestedServerNames == null) { + if (isDestroyed()) { + requestedServerNames = Collections.emptyList(); + } else { + String name = SSL.getSniHostname(ssl); + if (name == null) { + requestedServerNames = Collections.emptyList(); + } else { + // Convert to bytes as we do not want to do any strict validation of the + // SNIHostName while creating it. + requestedServerNames = + Java8SslUtils.getSniHostName( + SSL.getSniHostname(ssl).getBytes(CharsetUtil.UTF_8)); + } + } + } + return requestedServerNames; + } + } + } + + @Override + public String[] getPeerSupportedSignatureAlgorithms() { + synchronized (ReferenceCountedOpenSslEngine.this) { + if (peerSupportedSignatureAlgorithms == null) { + if (isDestroyed()) { + peerSupportedSignatureAlgorithms = EmptyArrays.EMPTY_STRINGS; + } else { + String[] algs = SSL.getSigAlgs(ssl); + if (algs == null) { + peerSupportedSignatureAlgorithms = EmptyArrays.EMPTY_STRINGS; + } else { + List algorithmList = new ArrayList(algs.length); + for (String alg: algs) { + String converted = SignatureAlgorithmConverter.toJavaName(alg); + if (converted != null) { + algorithmList.add(converted); + } + } + peerSupportedSignatureAlgorithms = algorithmList.toArray(new String[0]); + } + } + } + return peerSupportedSignatureAlgorithms.clone(); + } } @Override @@ -271,7 +321,6 @@ public List getStatusResponses() { } engineMap = context.engineMap; localCerts = context.keyCertChain; - keyMaterialManager = context.keyMaterialManager(); enableOcsp = context.enableOcsp; this.jdkCompatibilityMode = jdkCompatibilityMode; Lock readerLock = context.ctxLock.readLock(); @@ -1593,11 +1642,6 @@ private SSLEngineResult.HandshakeStatus handshake() throws SSLException { lastAccessed = System.currentTimeMillis(); } - if (!certificateSet && keyMaterialManager != null) { - certificateSet = true; - keyMaterialManager.setKeyMaterialServerSide(this); - } - int code = SSL.doHandshake(ssl); if (code <= 0) { // Check if we have a pending exception that was created during the handshake and if so throw it after diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java index b9c8cf60b8ce..1dbae15dd3ce 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java @@ -16,6 +16,7 @@ package io.netty.handler.ssl; import io.netty.buffer.ByteBufAllocator; +import io.netty.internal.tcnative.CertificateCallback; import io.netty.internal.tcnative.SSL; import io.netty.internal.tcnative.SSLContext; import io.netty.internal.tcnative.SniHostNameMatcher; @@ -29,6 +30,7 @@ import java.security.cert.X509Certificate; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; @@ -48,7 +50,6 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted InternalLoggerFactory.getInstance(ReferenceCountedOpenSslServerContext.class); private static final byte[] ID = {'n', 'e', 't', 't', 'y'}; private final OpenSslServerSessionContext sessionContext; - private final OpenSslKeyMaterialManager keyMaterialManager; ReferenceCountedOpenSslServerContext( X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, @@ -72,10 +73,8 @@ private ReferenceCountedOpenSslServerContext( // Create a new SSL_CTX and configure it. boolean success = false; try { - ServerContext context = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, + sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory); - sessionContext = context.sessionContext; - keyMaterialManager = context.keyMaterialManager; success = true; } finally { if (!success) { @@ -89,23 +88,13 @@ public OpenSslServerSessionContext sessionContext() { return sessionContext; } - @Override - OpenSslKeyMaterialManager keyMaterialManager() { - return keyMaterialManager; - } - - static final class ServerContext { - OpenSslServerSessionContext sessionContext; - OpenSslKeyMaterialManager keyMaterialManager; - } - - static ServerContext newSessionContext(ReferenceCountedOpenSslContext thiz, long ctx, OpenSslEngineMap engineMap, - X509Certificate[] trustCertCollection, - TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, - String keyPassword, KeyManagerFactory keyManagerFactory) + static OpenSslServerSessionContext newSessionContext(ReferenceCountedOpenSslContext thiz, long ctx, + OpenSslEngineMap engineMap, + X509Certificate[] trustCertCollection, + TrustManagerFactory trustManagerFactory, + X509Certificate[] keyCertChain, PrivateKey key, + String keyPassword, KeyManagerFactory keyManagerFactory) throws SSLException { - ServerContext result = new ServerContext(); OpenSslKeyMaterialProvider keyMaterialProvider = null; try { try { @@ -134,7 +123,8 @@ static ServerContext newSessionContext(ReferenceCountedOpenSslContext thiz, long } keyMaterialProvider = providerFor(keyManagerFactory, keyPassword); - result.keyMaterialManager = new OpenSslKeyMaterialManager(keyMaterialProvider); + SSLContext.setCertificateCallback(ctx, new OpenSslServerCertificateCallback( + engineMap, new OpenSslKeyMaterialManager(keyMaterialProvider))); } } catch (Exception e) { throw new SSLException("failed to set certificate and key", e); @@ -159,8 +149,8 @@ static ServerContext newSessionContext(ReferenceCountedOpenSslContext thiz, long // Use this to prevent an error when running on java < 7 if (useExtendedTrustManager(manager)) { - SSLContext.setCertVerifyCallback(ctx, - new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager)); + SSLContext.setCertVerifyCallback(ctx, new ExtendedTrustManagerVerifyCallback( + engineMap, (X509ExtendedTrustManager) manager)); } else { SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager)); } @@ -191,12 +181,12 @@ static ServerContext newSessionContext(ReferenceCountedOpenSslContext thiz, long throw new SSLException("unable to setup trustmanager", e); } - result.sessionContext = new OpenSslServerSessionContext(thiz, keyMaterialProvider); - result.sessionContext.setSessionIdContext(ID); + OpenSslServerSessionContext sessionContext = new OpenSslServerSessionContext(thiz, keyMaterialProvider); + sessionContext.setSessionIdContext(ID); keyMaterialProvider = null; - return result; + return sessionContext; } finally { if (keyMaterialProvider != null) { keyMaterialProvider.destroy(); @@ -204,6 +194,31 @@ static ServerContext newSessionContext(ReferenceCountedOpenSslContext thiz, long } } + private static final class OpenSslServerCertificateCallback implements CertificateCallback { + private final OpenSslEngineMap engineMap; + private final OpenSslKeyMaterialManager keyManagerHolder; + + OpenSslServerCertificateCallback(OpenSslEngineMap engineMap, OpenSslKeyMaterialManager keyManagerHolder) { + this.engineMap = engineMap; + this.keyManagerHolder = keyManagerHolder; + } + + @Override + public void handle(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) throws Exception { + final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); + try { + // For now we just ignore the asn1DerEncodedPrincipals as this is kind of inline with what the + // OpenJDK SSLEngineImpl does. + keyManagerHolder.setKeyMaterialServerSide(engine); + } catch (Throwable cause) { + logger.debug("Failed to set the server-side key material", cause); + SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem"); + e.initCause(cause); + engine.handshakeException = e; + } + } + } + private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier { private final X509TrustManager manager; diff --git a/handler/src/main/java/io/netty/handler/ssl/SignatureAlgorithmConverter.java b/handler/src/main/java/io/netty/handler/ssl/SignatureAlgorithmConverter.java new file mode 100644 index 000000000000..70a13516d44e --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/SignatureAlgorithmConverter.java @@ -0,0 +1,61 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Converts OpenSSL signature Algorithm names to + * + * Java signature Algorithm names. + */ +final class SignatureAlgorithmConverter { + + private SignatureAlgorithmConverter() { } + + // OpenSSL has 3 different formats it uses at the moment we will match against all of these. + // For example: + // ecdsa-with-SHA384 + // hmacWithSHA384 + // dsa_with_SHA224 + // + // For more details see https://github.com/openssl/openssl/blob/OpenSSL_1_0_2p/crypto/objects/obj_dat.h + private static final Pattern PATTERN = Pattern.compile( + "((^[a-zA-Z].+)With(.+)Encryption$)|((^[a-zA-Z].+)(_with_|-with-)(.+$))"); + + /** + * Converts an OpenSSL algorithm name to a Java algorithm name and return it, + * or return {@code null} if the conversation failed because the format is not known. + */ + static String toJavaName(String opensslName) { + if (opensslName == null) { + return null; + } + Matcher matcher = PATTERN.matcher(opensslName); + if (matcher.matches()) { + String group2 = matcher.group(2); + if (group2 != null) { + return group2.toUpperCase(Locale.ROOT) + "with" + matcher.group(3).toUpperCase(Locale.ROOT); + } + if (matcher.group(4) != null) { + return matcher.group(7).toUpperCase(Locale.ROOT) + "with" + matcher.group(5).toUpperCase(Locale.ROOT); + } + } + return null; + } +} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java index 7882a61b4f4b..e8c46ed7a056 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java @@ -24,4 +24,8 @@ private OpenSslTestUtils() { static void checkShouldUseKeyManagerFactory() { assumeTrue(OpenSsl.supportsKeyManagerFactory() && OpenSsl.useKeyManagerFactory()); } + + static boolean isBoringSSL() { + return "BoringSSL".equals(OpenSsl.versionString()); + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SignatureAlgorithmConverterTest.java b/handler/src/test/java/io/netty/handler/ssl/SignatureAlgorithmConverterTest.java new file mode 100644 index 000000000000..199b50b395f9 --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/SignatureAlgorithmConverterTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class SignatureAlgorithmConverterTest { + + @Test + public void testWithEncryption() { + assertEquals("SHA512withRSA", SignatureAlgorithmConverter.toJavaName("sha512WithRSAEncryption")); + } + + @Test + public void testWithDash() { + assertEquals("SHA256withECDSA", SignatureAlgorithmConverter.toJavaName("ecdsa-with-SHA256")); + } + + @Test + public void testWithUnderscore() { + assertEquals("SHA256withDSA", SignatureAlgorithmConverter.toJavaName("dsa_with_SHA256")); + } + + @Test + public void testInvalid() { + assertNull(SignatureAlgorithmConverter.toJavaName("ThisIsSomethingInvalid")); + } +} diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java index 3c7783785ef4..d033ca936590 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java @@ -64,6 +64,7 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -166,11 +167,11 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc } } - static void assertSSLSession(SSLSession session, String name) { - assertSSLSession(session, new SNIHostName(name)); + static void assertSSLSession(boolean clientSide, SSLSession session, String name) { + assertSSLSession(clientSide, session, new SNIHostName(name)); } - private static void assertSSLSession(SSLSession session, SNIServerName name) { + private static void assertSSLSession(boolean clientSide, SSLSession session, SNIServerName name) { Assert.assertNotNull(session); if (session instanceof ExtendedSSLSession) { ExtendedSSLSession extendedSSLSession = (ExtendedSSLSession) session; @@ -178,6 +179,17 @@ private static void assertSSLSession(SSLSession session, SNIServerName name) { Assert.assertEquals(1, names.size()); Assert.assertEquals(name, names.get(0)); Assert.assertTrue(extendedSSLSession.getLocalSupportedSignatureAlgorithms().length > 0); + if (clientSide) { + Assert.assertEquals(0, extendedSSLSession.getPeerSupportedSignatureAlgorithms().length); + } else { + if (session instanceof OpenSslSession && OpenSslTestUtils.isBoringSSL()) { + // BoringSSL does not support SSL_get_sigalgs(...) + // https://boringssl.googlesource.com/boringssl/+/ba16a1e405c617f4179bd780ad15522fb25b0a65%5E%21/ + Assert.assertEquals(0, extendedSSLSession.getPeerSupportedSignatureAlgorithms().length); + } else { + Assert.assertTrue(extendedSSLSession.getPeerSupportedSignatureAlgorithms().length > 0); + } + } } } @@ -227,7 +239,7 @@ public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSL @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException { - assertSSLSession(sslEngine.getHandshakeSession(), name); + assertSSLSession(sslEngine.getUseClientMode(), sslEngine.getHandshakeSession(), name); } @Override @@ -323,7 +335,7 @@ public String chooseEngineServerAlias(String s, Principal[] principals, SSLEngine sslEngine) { SSLSession session = sslEngine.getHandshakeSession(); - assertSSLSession(session, name); + assertSSLSession(sslEngine.getUseClientMode(), session, name); return ((X509ExtendedKeyManager) km) .chooseEngineServerAlias(s, principals, sslEngine); } diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java index 7cb473150f66..56ea815eb85b 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java @@ -155,7 +155,8 @@ public SslContext map(String input) { Assert.assertNull(handler.engine().getHandshakeSession()); if (PlatformDependent.javaVersion() >= 8) { - SniClientJava8TestUtil.assertSSLSession(handler.engine().getSession(), sniHostName); + SniClientJava8TestUtil.assertSSLSession( + handler.engine().getUseClientMode(), handler.engine().getSession(), sniHostName); } } finally { if (cc != null) { diff --git a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java index 6285ae0c4ef3..d935cdf683df 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java @@ -204,7 +204,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { verifyException(unwrappedCause, "expired", promise); } else if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) { // BoringSSL uses "expired" in this case while others use "bad" - if ("BoringSSL".equals(OpenSsl.versionString())) { + if (OpenSslTestUtils.isBoringSSL()) { verifyException(unwrappedCause, "expired", promise); } else { verifyException(unwrappedCause, "bad", promise); @@ -216,7 +216,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { verifyException(unwrappedCause, "expired", promise); } else if (exception instanceof CertificateNotYetValidException) { // BoringSSL uses "expired" in this case while others use "bad" - if ("BoringSSL".equals(OpenSsl.versionString())) { + if (OpenSslTestUtils.isBoringSSL()) { verifyException(unwrappedCause, "expired", promise); } else { verifyException(unwrappedCause, "bad", promise); diff --git a/pom.xml b/pom.xml index 0aa5dd28ad36..6c42c7977b07 100644 --- a/pom.xml +++ b/pom.xml @@ -241,7 +241,7 @@ fedora netty-tcnative - 2.0.15.Final + 2.0.17.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber From 6138541033ee20fbef864dc5535bcd0db370593e Mon Sep 17 00:00:00 2001 From: Roger Date: Fri, 28 Sep 2018 11:37:14 -0400 Subject: [PATCH 194/417] Avoid repeating the same field and hiding it (#8335) Motivation The EpollChannelConfig (same for KQueues) and its subclasses repeatetly declare their own channel field which leads to a 3x repetition for each config instance. Given the fields are protected or package-private it's exposing the code code to "field hiding" bugs. Modifications Use the the existing protected channel field from the DefaultChannelConfig class and simply cast it when needed. Result Fixes #8331 --- .../channel/epoll/EpollChannelConfig.java | 10 ++- .../epoll/EpollDatagramChannelConfig.java | 34 +++++---- .../epoll/EpollServerChannelConfig.java | 10 ++- .../epoll/EpollServerSocketChannelConfig.java | 16 ++--- .../epoll/EpollSocketChannelConfig.java | 72 +++++++++---------- .../channel/kqueue/KQueueChannelConfig.java | 4 +- .../kqueue/KQueueDatagramChannelConfig.java | 26 ++++--- .../kqueue/KQueueServerChannelConfig.java | 10 ++- .../KQueueServerSocketChannelConfig.java | 8 +-- .../kqueue/KQueueSocketChannelConfig.java | 38 +++++----- 10 files changed, 106 insertions(+), 122 deletions(-) diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelConfig.java index c0cceb255afc..2d2610c0e27b 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelConfig.java @@ -29,12 +29,10 @@ import static io.netty.channel.unix.Limits.SSIZE_MAX; public class EpollChannelConfig extends DefaultChannelConfig { - final AbstractEpollChannel channel; private volatile long maxBytesPerGatheringWrite = SSIZE_MAX; EpollChannelConfig(AbstractEpollChannel channel) { super(channel); - this.channel = channel; } @Override @@ -136,7 +134,7 @@ public EpollChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator * {@link EpollMode#LEVEL_TRIGGERED}. */ public EpollMode getEpollMode() { - return channel.isFlagSet(Native.EPOLLET) + return ((AbstractEpollChannel) channel).isFlagSet(Native.EPOLLET) ? EpollMode.EDGE_TRIGGERED : EpollMode.LEVEL_TRIGGERED; } @@ -156,11 +154,11 @@ public EpollChannelConfig setEpollMode(EpollMode mode) { switch (mode) { case EDGE_TRIGGERED: checkChannelNotRegistered(); - channel.setFlag(Native.EPOLLET); + ((AbstractEpollChannel) channel).setFlag(Native.EPOLLET); break; case LEVEL_TRIGGERED: checkChannelNotRegistered(); - channel.clearFlag(Native.EPOLLET); + ((AbstractEpollChannel) channel).clearFlag(Native.EPOLLET); break; default: throw new Error(); @@ -179,7 +177,7 @@ private void checkChannelNotRegistered() { @Override protected final void autoReadCleared() { - channel.clearEpollIn(); + ((AbstractEpollChannel) channel).clearEpollIn(); } final void setMaxBytesPerGatheringWrite(long maxBytesPerGatheringWrite) { diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java index fbc44c1bcc16..f3de6ac5947b 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java @@ -31,12 +31,10 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig implements DatagramChannelConfig { private static final RecvByteBufAllocator DEFAULT_RCVBUF_ALLOCATOR = new FixedRecvByteBufAllocator(2048); - private final EpollDatagramChannel datagramChannel; private boolean activeOnOpen; EpollDatagramChannelConfig(EpollDatagramChannel channel) { super(channel); - datagramChannel = channel; setRecvByteBufAllocator(DEFAULT_RCVBUF_ALLOCATOR); } @@ -219,7 +217,7 @@ public EpollDatagramChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) @Override public int getSendBufferSize() { try { - return datagramChannel.socket.getSendBufferSize(); + return ((EpollDatagramChannel) channel).socket.getSendBufferSize(); } catch (IOException e) { throw new ChannelException(e); } @@ -228,7 +226,7 @@ public int getSendBufferSize() { @Override public EpollDatagramChannelConfig setSendBufferSize(int sendBufferSize) { try { - datagramChannel.socket.setSendBufferSize(sendBufferSize); + ((EpollDatagramChannel) channel).socket.setSendBufferSize(sendBufferSize); return this; } catch (IOException e) { throw new ChannelException(e); @@ -238,7 +236,7 @@ public EpollDatagramChannelConfig setSendBufferSize(int sendBufferSize) { @Override public int getReceiveBufferSize() { try { - return datagramChannel.socket.getReceiveBufferSize(); + return ((EpollDatagramChannel) channel).socket.getReceiveBufferSize(); } catch (IOException e) { throw new ChannelException(e); } @@ -247,7 +245,7 @@ public int getReceiveBufferSize() { @Override public EpollDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) { try { - datagramChannel.socket.setReceiveBufferSize(receiveBufferSize); + ((EpollDatagramChannel) channel).socket.setReceiveBufferSize(receiveBufferSize); return this; } catch (IOException e) { throw new ChannelException(e); @@ -257,7 +255,7 @@ public EpollDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) { @Override public int getTrafficClass() { try { - return datagramChannel.socket.getTrafficClass(); + return ((EpollDatagramChannel) channel).socket.getTrafficClass(); } catch (IOException e) { throw new ChannelException(e); } @@ -266,7 +264,7 @@ public int getTrafficClass() { @Override public EpollDatagramChannelConfig setTrafficClass(int trafficClass) { try { - datagramChannel.socket.setTrafficClass(trafficClass); + ((EpollDatagramChannel) channel).socket.setTrafficClass(trafficClass); return this; } catch (IOException e) { throw new ChannelException(e); @@ -276,7 +274,7 @@ public EpollDatagramChannelConfig setTrafficClass(int trafficClass) { @Override public boolean isReuseAddress() { try { - return datagramChannel.socket.isReuseAddress(); + return ((EpollDatagramChannel) channel).socket.isReuseAddress(); } catch (IOException e) { throw new ChannelException(e); } @@ -285,7 +283,7 @@ public boolean isReuseAddress() { @Override public EpollDatagramChannelConfig setReuseAddress(boolean reuseAddress) { try { - datagramChannel.socket.setReuseAddress(reuseAddress); + ((EpollDatagramChannel) channel).socket.setReuseAddress(reuseAddress); return this; } catch (IOException e) { throw new ChannelException(e); @@ -295,7 +293,7 @@ public EpollDatagramChannelConfig setReuseAddress(boolean reuseAddress) { @Override public boolean isBroadcast() { try { - return datagramChannel.socket.isBroadcast(); + return ((EpollDatagramChannel) channel).socket.isBroadcast(); } catch (IOException e) { throw new ChannelException(e); } @@ -304,7 +302,7 @@ public boolean isBroadcast() { @Override public EpollDatagramChannelConfig setBroadcast(boolean broadcast) { try { - datagramChannel.socket.setBroadcast(broadcast); + ((EpollDatagramChannel) channel).socket.setBroadcast(broadcast); return this; } catch (IOException e) { throw new ChannelException(e); @@ -362,7 +360,7 @@ public EpollDatagramChannelConfig setEpollMode(EpollMode mode) { */ public boolean isReusePort() { try { - return datagramChannel.socket.isReusePort(); + return ((EpollDatagramChannel) channel).socket.isReusePort(); } catch (IOException e) { throw new ChannelException(e); } @@ -377,7 +375,7 @@ public boolean isReusePort() { */ public EpollDatagramChannelConfig setReusePort(boolean reusePort) { try { - datagramChannel.socket.setReusePort(reusePort); + ((EpollDatagramChannel) channel).socket.setReusePort(reusePort); return this; } catch (IOException e) { throw new ChannelException(e); @@ -390,7 +388,7 @@ public EpollDatagramChannelConfig setReusePort(boolean reusePort) { */ public boolean isIpTransparent() { try { - return datagramChannel.socket.isIpTransparent(); + return ((EpollDatagramChannel) channel).socket.isIpTransparent(); } catch (IOException e) { throw new ChannelException(e); } @@ -402,7 +400,7 @@ public boolean isIpTransparent() { */ public EpollDatagramChannelConfig setIpTransparent(boolean ipTransparent) { try { - datagramChannel.socket.setIpTransparent(ipTransparent); + ((EpollDatagramChannel) channel).socket.setIpTransparent(ipTransparent); return this; } catch (IOException e) { throw new ChannelException(e); @@ -415,7 +413,7 @@ public EpollDatagramChannelConfig setIpTransparent(boolean ipTransparent) { */ public boolean isIpRecvOrigDestAddr() { try { - return datagramChannel.socket.isIpRecvOrigDestAddr(); + return ((EpollDatagramChannel) channel).socket.isIpRecvOrigDestAddr(); } catch (IOException e) { throw new ChannelException(e); } @@ -427,7 +425,7 @@ public boolean isIpRecvOrigDestAddr() { */ public EpollDatagramChannelConfig setIpRecvOrigDestAddr(boolean ipTransparent) { try { - datagramChannel.socket.setIpRecvOrigDestAddr(ipTransparent); + ((EpollDatagramChannel) channel).socket.setIpRecvOrigDestAddr(ipTransparent); return this; } catch (IOException e) { throw new ChannelException(e); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java index 5d6394b1428c..514b6c34cc4e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java @@ -32,13 +32,11 @@ import static io.netty.channel.ChannelOption.SO_REUSEADDR; public class EpollServerChannelConfig extends EpollChannelConfig implements ServerSocketChannelConfig { - protected final AbstractEpollChannel channel; private volatile int backlog = NetUtil.SOMAXCONN; private volatile int pendingFastOpenRequestsThreshold; EpollServerChannelConfig(AbstractEpollChannel channel) { super(channel); - this.channel = channel; } @Override @@ -85,7 +83,7 @@ public boolean setOption(ChannelOption option, T value) { public boolean isReuseAddress() { try { - return channel.socket.isReuseAddress(); + return ((AbstractEpollChannel) channel).socket.isReuseAddress(); } catch (IOException e) { throw new ChannelException(e); } @@ -93,7 +91,7 @@ public boolean isReuseAddress() { public EpollServerChannelConfig setReuseAddress(boolean reuseAddress) { try { - channel.socket.setReuseAddress(reuseAddress); + ((AbstractEpollChannel) channel).socket.setReuseAddress(reuseAddress); return this; } catch (IOException e) { throw new ChannelException(e); @@ -102,7 +100,7 @@ public EpollServerChannelConfig setReuseAddress(boolean reuseAddress) { public int getReceiveBufferSize() { try { - return channel.socket.getReceiveBufferSize(); + return ((AbstractEpollChannel) channel).socket.getReceiveBufferSize(); } catch (IOException e) { throw new ChannelException(e); } @@ -110,7 +108,7 @@ public int getReceiveBufferSize() { public EpollServerChannelConfig setReceiveBufferSize(int receiveBufferSize) { try { - channel.socket.setReceiveBufferSize(receiveBufferSize); + ((AbstractEpollChannel) channel).socket.setReceiveBufferSize(receiveBufferSize); return this; } catch (IOException e) { throw new ChannelException(e); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannelConfig.java index dfccb199c983..91861f0c137e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannelConfig.java @@ -191,7 +191,7 @@ public EpollServerSocketChannelConfig setTcpMd5Sig(Map keys */ public boolean isReusePort() { try { - return channel.socket.isReusePort(); + return ((EpollServerSocketChannel) channel).socket.isReusePort(); } catch (IOException e) { throw new ChannelException(e); } @@ -206,7 +206,7 @@ public boolean isReusePort() { */ public EpollServerSocketChannelConfig setReusePort(boolean reusePort) { try { - channel.socket.setReusePort(reusePort); + ((EpollServerSocketChannel) channel).socket.setReusePort(reusePort); return this; } catch (IOException e) { throw new ChannelException(e); @@ -219,7 +219,7 @@ public EpollServerSocketChannelConfig setReusePort(boolean reusePort) { */ public boolean isFreeBind() { try { - return channel.socket.isIpFreeBind(); + return ((EpollServerSocketChannel) channel).socket.isIpFreeBind(); } catch (IOException e) { throw new ChannelException(e); } @@ -231,7 +231,7 @@ public boolean isFreeBind() { */ public EpollServerSocketChannelConfig setFreeBind(boolean freeBind) { try { - channel.socket.setIpFreeBind(freeBind); + ((EpollServerSocketChannel) channel).socket.setIpFreeBind(freeBind); return this; } catch (IOException e) { throw new ChannelException(e); @@ -244,7 +244,7 @@ public EpollServerSocketChannelConfig setFreeBind(boolean freeBind) { */ public boolean isIpTransparent() { try { - return channel.socket.isIpTransparent(); + return ((EpollServerSocketChannel) channel).socket.isIpTransparent(); } catch (IOException e) { throw new ChannelException(e); } @@ -256,7 +256,7 @@ public boolean isIpTransparent() { */ public EpollServerSocketChannelConfig setIpTransparent(boolean transparent) { try { - channel.socket.setIpTransparent(transparent); + ((EpollServerSocketChannel) channel).socket.setIpTransparent(transparent); return this; } catch (IOException e) { throw new ChannelException(e); @@ -268,7 +268,7 @@ public EpollServerSocketChannelConfig setIpTransparent(boolean transparent) { */ public EpollServerSocketChannelConfig setTcpDeferAccept(int deferAccept) { try { - channel.socket.setTcpDeferAccept(deferAccept); + ((EpollServerSocketChannel) channel).socket.setTcpDeferAccept(deferAccept); return this; } catch (IOException e) { throw new ChannelException(e); @@ -280,7 +280,7 @@ public EpollServerSocketChannelConfig setTcpDeferAccept(int deferAccept) { */ public int getTcpDeferAccept() { try { - return channel.socket.getTcpDeferAccept(); + return ((EpollServerSocketChannel) channel).socket.getTcpDeferAccept(); } catch (IOException e) { throw new ChannelException(e); } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java index d61bf19e27e9..f3d04dd3f955 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java @@ -38,7 +38,6 @@ import static io.netty.channel.ChannelOption.TCP_NODELAY; public final class EpollSocketChannelConfig extends EpollChannelConfig implements SocketChannelConfig { - private final EpollSocketChannel channel; private volatile boolean allowHalfClosure; /** @@ -47,7 +46,6 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement EpollSocketChannelConfig(EpollSocketChannel channel) { super(channel); - this.channel = channel; if (PlatformDependent.canEnableTcpNoDelayByDefault()) { setTcpNoDelay(true); } @@ -179,7 +177,7 @@ public boolean setOption(ChannelOption option, T value) { @Override public int getReceiveBufferSize() { try { - return channel.socket.getReceiveBufferSize(); + return ((EpollSocketChannel) channel).socket.getReceiveBufferSize(); } catch (IOException e) { throw new ChannelException(e); } @@ -188,7 +186,7 @@ public int getReceiveBufferSize() { @Override public int getSendBufferSize() { try { - return channel.socket.getSendBufferSize(); + return ((EpollSocketChannel) channel).socket.getSendBufferSize(); } catch (IOException e) { throw new ChannelException(e); } @@ -197,7 +195,7 @@ public int getSendBufferSize() { @Override public int getSoLinger() { try { - return channel.socket.getSoLinger(); + return ((EpollSocketChannel) channel).socket.getSoLinger(); } catch (IOException e) { throw new ChannelException(e); } @@ -206,7 +204,7 @@ public int getSoLinger() { @Override public int getTrafficClass() { try { - return channel.socket.getTrafficClass(); + return ((EpollSocketChannel) channel).socket.getTrafficClass(); } catch (IOException e) { throw new ChannelException(e); } @@ -215,7 +213,7 @@ public int getTrafficClass() { @Override public boolean isKeepAlive() { try { - return channel.socket.isKeepAlive(); + return ((EpollSocketChannel) channel).socket.isKeepAlive(); } catch (IOException e) { throw new ChannelException(e); } @@ -224,7 +222,7 @@ public boolean isKeepAlive() { @Override public boolean isReuseAddress() { try { - return channel.socket.isReuseAddress(); + return ((EpollSocketChannel) channel).socket.isReuseAddress(); } catch (IOException e) { throw new ChannelException(e); } @@ -233,7 +231,7 @@ public boolean isReuseAddress() { @Override public boolean isTcpNoDelay() { try { - return channel.socket.isTcpNoDelay(); + return ((EpollSocketChannel) channel).socket.isTcpNoDelay(); } catch (IOException e) { throw new ChannelException(e); } @@ -244,7 +242,7 @@ public boolean isTcpNoDelay() { */ public boolean isTcpCork() { try { - return channel.socket.isTcpCork(); + return ((EpollSocketChannel) channel).socket.isTcpCork(); } catch (IOException e) { throw new ChannelException(e); } @@ -255,7 +253,7 @@ public boolean isTcpCork() { */ public int getSoBusyPoll() { try { - return channel.socket.getSoBusyPoll(); + return ((EpollSocketChannel) channel).socket.getSoBusyPoll(); } catch (IOException e) { throw new ChannelException(e); } @@ -267,7 +265,7 @@ public int getSoBusyPoll() { */ public long getTcpNotSentLowAt() { try { - return channel.socket.getTcpNotSentLowAt(); + return ((EpollSocketChannel) channel).socket.getTcpNotSentLowAt(); } catch (IOException e) { throw new ChannelException(e); } @@ -278,7 +276,7 @@ public long getTcpNotSentLowAt() { */ public int getTcpKeepIdle() { try { - return channel.socket.getTcpKeepIdle(); + return ((EpollSocketChannel) channel).socket.getTcpKeepIdle(); } catch (IOException e) { throw new ChannelException(e); } @@ -289,7 +287,7 @@ public int getTcpKeepIdle() { */ public int getTcpKeepIntvl() { try { - return channel.socket.getTcpKeepIntvl(); + return ((EpollSocketChannel) channel).socket.getTcpKeepIntvl(); } catch (IOException e) { throw new ChannelException(e); } @@ -300,7 +298,7 @@ public int getTcpKeepIntvl() { */ public int getTcpKeepCnt() { try { - return channel.socket.getTcpKeepCnt(); + return ((EpollSocketChannel) channel).socket.getTcpKeepCnt(); } catch (IOException e) { throw new ChannelException(e); } @@ -311,7 +309,7 @@ public int getTcpKeepCnt() { */ public int getTcpUserTimeout() { try { - return channel.socket.getTcpUserTimeout(); + return ((EpollSocketChannel) channel).socket.getTcpUserTimeout(); } catch (IOException e) { throw new ChannelException(e); } @@ -320,7 +318,7 @@ public int getTcpUserTimeout() { @Override public EpollSocketChannelConfig setKeepAlive(boolean keepAlive) { try { - channel.socket.setKeepAlive(keepAlive); + ((EpollSocketChannel) channel).socket.setKeepAlive(keepAlive); return this; } catch (IOException e) { throw new ChannelException(e); @@ -336,7 +334,7 @@ public EpollSocketChannelConfig setPerformancePreferences( @Override public EpollSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) { try { - channel.socket.setReceiveBufferSize(receiveBufferSize); + ((EpollSocketChannel) channel).socket.setReceiveBufferSize(receiveBufferSize); return this; } catch (IOException e) { throw new ChannelException(e); @@ -346,7 +344,7 @@ public EpollSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) { @Override public EpollSocketChannelConfig setReuseAddress(boolean reuseAddress) { try { - channel.socket.setReuseAddress(reuseAddress); + ((EpollSocketChannel) channel).socket.setReuseAddress(reuseAddress); return this; } catch (IOException e) { throw new ChannelException(e); @@ -356,7 +354,7 @@ public EpollSocketChannelConfig setReuseAddress(boolean reuseAddress) { @Override public EpollSocketChannelConfig setSendBufferSize(int sendBufferSize) { try { - channel.socket.setSendBufferSize(sendBufferSize); + ((EpollSocketChannel) channel).socket.setSendBufferSize(sendBufferSize); calculateMaxBytesPerGatheringWrite(); return this; } catch (IOException e) { @@ -367,7 +365,7 @@ public EpollSocketChannelConfig setSendBufferSize(int sendBufferSize) { @Override public EpollSocketChannelConfig setSoLinger(int soLinger) { try { - channel.socket.setSoLinger(soLinger); + ((EpollSocketChannel) channel).socket.setSoLinger(soLinger); return this; } catch (IOException e) { throw new ChannelException(e); @@ -377,7 +375,7 @@ public EpollSocketChannelConfig setSoLinger(int soLinger) { @Override public EpollSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) { try { - channel.socket.setTcpNoDelay(tcpNoDelay); + ((EpollSocketChannel) channel).socket.setTcpNoDelay(tcpNoDelay); return this; } catch (IOException e) { throw new ChannelException(e); @@ -389,7 +387,7 @@ public EpollSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) { */ public EpollSocketChannelConfig setTcpCork(boolean tcpCork) { try { - channel.socket.setTcpCork(tcpCork); + ((EpollSocketChannel) channel).socket.setTcpCork(tcpCork); return this; } catch (IOException e) { throw new ChannelException(e); @@ -401,7 +399,7 @@ public EpollSocketChannelConfig setTcpCork(boolean tcpCork) { */ public EpollSocketChannelConfig setSoBusyPoll(int loopMicros) { try { - channel.socket.setSoBusyPoll(loopMicros); + ((EpollSocketChannel) channel).socket.setSoBusyPoll(loopMicros); return this; } catch (IOException e) { throw new ChannelException(e); @@ -414,7 +412,7 @@ public EpollSocketChannelConfig setSoBusyPoll(int loopMicros) { */ public EpollSocketChannelConfig setTcpNotSentLowAt(long tcpNotSentLowAt) { try { - channel.socket.setTcpNotSentLowAt(tcpNotSentLowAt); + ((EpollSocketChannel) channel).socket.setTcpNotSentLowAt(tcpNotSentLowAt); return this; } catch (IOException e) { throw new ChannelException(e); @@ -424,7 +422,7 @@ public EpollSocketChannelConfig setTcpNotSentLowAt(long tcpNotSentLowAt) { @Override public EpollSocketChannelConfig setTrafficClass(int trafficClass) { try { - channel.socket.setTrafficClass(trafficClass); + ((EpollSocketChannel) channel).socket.setTrafficClass(trafficClass); return this; } catch (IOException e) { throw new ChannelException(e); @@ -436,7 +434,7 @@ public EpollSocketChannelConfig setTrafficClass(int trafficClass) { */ public EpollSocketChannelConfig setTcpKeepIdle(int seconds) { try { - channel.socket.setTcpKeepIdle(seconds); + ((EpollSocketChannel) channel).socket.setTcpKeepIdle(seconds); return this; } catch (IOException e) { throw new ChannelException(e); @@ -448,7 +446,7 @@ public EpollSocketChannelConfig setTcpKeepIdle(int seconds) { */ public EpollSocketChannelConfig setTcpKeepIntvl(int seconds) { try { - channel.socket.setTcpKeepIntvl(seconds); + ((EpollSocketChannel) channel).socket.setTcpKeepIntvl(seconds); return this; } catch (IOException e) { throw new ChannelException(e); @@ -468,7 +466,7 @@ public EpollSocketChannelConfig setTcpKeepCntl(int probes) { */ public EpollSocketChannelConfig setTcpKeepCnt(int probes) { try { - channel.socket.setTcpKeepCnt(probes); + ((EpollSocketChannel) channel).socket.setTcpKeepCnt(probes); return this; } catch (IOException e) { throw new ChannelException(e); @@ -480,7 +478,7 @@ public EpollSocketChannelConfig setTcpKeepCnt(int probes) { */ public EpollSocketChannelConfig setTcpUserTimeout(int milliseconds) { try { - channel.socket.setTcpUserTimeout(milliseconds); + ((EpollSocketChannel) channel).socket.setTcpUserTimeout(milliseconds); return this; } catch (IOException e) { throw new ChannelException(e); @@ -493,7 +491,7 @@ public EpollSocketChannelConfig setTcpUserTimeout(int milliseconds) { */ public boolean isIpTransparent() { try { - return channel.socket.isIpTransparent(); + return ((EpollSocketChannel) channel).socket.isIpTransparent(); } catch (IOException e) { throw new ChannelException(e); } @@ -505,7 +503,7 @@ public boolean isIpTransparent() { */ public EpollSocketChannelConfig setIpTransparent(boolean transparent) { try { - channel.socket.setIpTransparent(transparent); + ((EpollSocketChannel) channel).socket.setIpTransparent(transparent); return this; } catch (IOException e) { throw new ChannelException(e); @@ -519,7 +517,7 @@ public EpollSocketChannelConfig setIpTransparent(boolean transparent) { */ public EpollSocketChannelConfig setTcpMd5Sig(Map keys) { try { - channel.setTcpMd5Sig(keys); + ((EpollSocketChannel) channel).setTcpMd5Sig(keys); return this; } catch (IOException e) { throw new ChannelException(e); @@ -532,7 +530,7 @@ public EpollSocketChannelConfig setTcpMd5Sig(Map keys) { */ public EpollSocketChannelConfig setTcpQuickAck(boolean quickAck) { try { - channel.socket.setTcpQuickAck(quickAck); + ((EpollSocketChannel) channel).socket.setTcpQuickAck(quickAck); return this; } catch (IOException e) { throw new ChannelException(e); @@ -545,7 +543,7 @@ public EpollSocketChannelConfig setTcpQuickAck(boolean quickAck) { */ public boolean isTcpQuickAck() { try { - return channel.socket.isTcpQuickAck(); + return ((EpollSocketChannel) channel).socket.isTcpQuickAck(); } catch (IOException e) { throw new ChannelException(e); } @@ -559,7 +557,7 @@ public boolean isTcpQuickAck() { */ public EpollSocketChannelConfig setTcpFastOpenConnect(boolean fastOpenConnect) { try { - channel.socket.setTcpFastOpenConnect(fastOpenConnect); + ((EpollSocketChannel) channel).socket.setTcpFastOpenConnect(fastOpenConnect); return this; } catch (IOException e) { throw new ChannelException(e); @@ -571,7 +569,7 @@ public EpollSocketChannelConfig setTcpFastOpenConnect(boolean fastOpenConnect) { */ public boolean isTcpFastOpenConnect() { try { - return channel.socket.isTcpFastOpenConnect(); + return ((EpollSocketChannel) channel).socket.isTcpFastOpenConnect(); } catch (IOException e) { throw new ChannelException(e); } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelConfig.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelConfig.java index 878663c5e746..c2b1debe983a 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelConfig.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelConfig.java @@ -31,13 +31,11 @@ @UnstableApi public class KQueueChannelConfig extends DefaultChannelConfig { - final AbstractKQueueChannel channel; private volatile boolean transportProvidesGuess; private volatile long maxBytesPerGatheringWrite = SSIZE_MAX; KQueueChannelConfig(AbstractKQueueChannel channel) { super(channel); - this.channel = channel; } @Override @@ -154,7 +152,7 @@ public KQueueChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimato @Override protected final void autoReadCleared() { - channel.clearReadFilter(); + ((AbstractKQueueChannel) channel).clearReadFilter(); } final void setMaxBytesPerGatheringWrite(long maxBytesPerGatheringWrite) { diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueDatagramChannelConfig.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueDatagramChannelConfig.java index c64417485b96..478d5544d164 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueDatagramChannelConfig.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueDatagramChannelConfig.java @@ -45,12 +45,10 @@ @UnstableApi public final class KQueueDatagramChannelConfig extends KQueueChannelConfig implements DatagramChannelConfig { private static final RecvByteBufAllocator DEFAULT_RCVBUF_ALLOCATOR = new FixedRecvByteBufAllocator(2048); - private final KQueueDatagramChannel datagramChannel; private boolean activeOnOpen; KQueueDatagramChannelConfig(KQueueDatagramChannel channel) { super(channel); - this.datagramChannel = channel; setRecvByteBufAllocator(DEFAULT_RCVBUF_ALLOCATOR); } @@ -153,7 +151,7 @@ boolean getActiveOnOpen() { */ public boolean isReusePort() { try { - return datagramChannel.socket.isReusePort(); + return ((KQueueDatagramChannel) channel).socket.isReusePort(); } catch (IOException e) { throw new ChannelException(e); } @@ -168,7 +166,7 @@ public boolean isReusePort() { */ public KQueueDatagramChannelConfig setReusePort(boolean reusePort) { try { - datagramChannel.socket.setReusePort(reusePort); + ((KQueueDatagramChannel) channel).socket.setReusePort(reusePort); return this; } catch (IOException e) { throw new ChannelException(e); @@ -253,7 +251,7 @@ public KQueueDatagramChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) @Override public int getSendBufferSize() { try { - return datagramChannel.socket.getSendBufferSize(); + return ((KQueueDatagramChannel) channel).socket.getSendBufferSize(); } catch (IOException e) { throw new ChannelException(e); } @@ -262,7 +260,7 @@ public int getSendBufferSize() { @Override public KQueueDatagramChannelConfig setSendBufferSize(int sendBufferSize) { try { - datagramChannel.socket.setSendBufferSize(sendBufferSize); + ((KQueueDatagramChannel) channel).socket.setSendBufferSize(sendBufferSize); return this; } catch (IOException e) { throw new ChannelException(e); @@ -272,7 +270,7 @@ public KQueueDatagramChannelConfig setSendBufferSize(int sendBufferSize) { @Override public int getReceiveBufferSize() { try { - return datagramChannel.socket.getReceiveBufferSize(); + return ((KQueueDatagramChannel) channel).socket.getReceiveBufferSize(); } catch (IOException e) { throw new ChannelException(e); } @@ -281,7 +279,7 @@ public int getReceiveBufferSize() { @Override public KQueueDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) { try { - datagramChannel.socket.setReceiveBufferSize(receiveBufferSize); + ((KQueueDatagramChannel) channel).socket.setReceiveBufferSize(receiveBufferSize); return this; } catch (IOException e) { throw new ChannelException(e); @@ -291,7 +289,7 @@ public KQueueDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) { @Override public int getTrafficClass() { try { - return datagramChannel.socket.getTrafficClass(); + return ((KQueueDatagramChannel) channel).socket.getTrafficClass(); } catch (IOException e) { throw new ChannelException(e); } @@ -300,7 +298,7 @@ public int getTrafficClass() { @Override public KQueueDatagramChannelConfig setTrafficClass(int trafficClass) { try { - datagramChannel.socket.setTrafficClass(trafficClass); + ((KQueueDatagramChannel) channel).socket.setTrafficClass(trafficClass); return this; } catch (IOException e) { throw new ChannelException(e); @@ -310,7 +308,7 @@ public KQueueDatagramChannelConfig setTrafficClass(int trafficClass) { @Override public boolean isReuseAddress() { try { - return datagramChannel.socket.isReuseAddress(); + return ((KQueueDatagramChannel) channel).socket.isReuseAddress(); } catch (IOException e) { throw new ChannelException(e); } @@ -319,7 +317,7 @@ public boolean isReuseAddress() { @Override public KQueueDatagramChannelConfig setReuseAddress(boolean reuseAddress) { try { - datagramChannel.socket.setReuseAddress(reuseAddress); + ((KQueueDatagramChannel) channel).socket.setReuseAddress(reuseAddress); return this; } catch (IOException e) { throw new ChannelException(e); @@ -329,7 +327,7 @@ public KQueueDatagramChannelConfig setReuseAddress(boolean reuseAddress) { @Override public boolean isBroadcast() { try { - return datagramChannel.socket.isBroadcast(); + return ((KQueueDatagramChannel) channel).socket.isBroadcast(); } catch (IOException e) { throw new ChannelException(e); } @@ -338,7 +336,7 @@ public boolean isBroadcast() { @Override public KQueueDatagramChannelConfig setBroadcast(boolean broadcast) { try { - datagramChannel.socket.setBroadcast(broadcast); + ((KQueueDatagramChannel) channel).socket.setBroadcast(broadcast); return this; } catch (IOException e) { throw new ChannelException(e); diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerChannelConfig.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerChannelConfig.java index 7f878dc25779..09291f58dbf9 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerChannelConfig.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerChannelConfig.java @@ -34,12 +34,10 @@ @UnstableApi public class KQueueServerChannelConfig extends KQueueChannelConfig implements ServerSocketChannelConfig { - protected final AbstractKQueueChannel channel; private volatile int backlog = NetUtil.SOMAXCONN; KQueueServerChannelConfig(AbstractKQueueChannel channel) { super(channel); - this.channel = channel; } @Override @@ -81,7 +79,7 @@ public boolean setOption(ChannelOption option, T value) { public boolean isReuseAddress() { try { - return channel.socket.isReuseAddress(); + return ((AbstractKQueueChannel) channel).socket.isReuseAddress(); } catch (IOException e) { throw new ChannelException(e); } @@ -89,7 +87,7 @@ public boolean isReuseAddress() { public KQueueServerChannelConfig setReuseAddress(boolean reuseAddress) { try { - channel.socket.setReuseAddress(reuseAddress); + ((AbstractKQueueChannel) channel).socket.setReuseAddress(reuseAddress); return this; } catch (IOException e) { throw new ChannelException(e); @@ -98,7 +96,7 @@ public KQueueServerChannelConfig setReuseAddress(boolean reuseAddress) { public int getReceiveBufferSize() { try { - return channel.socket.getReceiveBufferSize(); + return ((AbstractKQueueChannel) channel).socket.getReceiveBufferSize(); } catch (IOException e) { throw new ChannelException(e); } @@ -106,7 +104,7 @@ public int getReceiveBufferSize() { public KQueueServerChannelConfig setReceiveBufferSize(int receiveBufferSize) { try { - channel.socket.setReceiveBufferSize(receiveBufferSize); + ((AbstractKQueueChannel) channel).socket.setReceiveBufferSize(receiveBufferSize); return this; } catch (IOException e) { throw new ChannelException(e); diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerSocketChannelConfig.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerSocketChannelConfig.java index dce3e6e71b85..a743e039de6f 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerSocketChannelConfig.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerSocketChannelConfig.java @@ -75,7 +75,7 @@ public boolean setOption(ChannelOption option, T value) { public KQueueServerSocketChannelConfig setReusePort(boolean reusePort) { try { - channel.socket.setReusePort(reusePort); + ((KQueueServerSocketChannel) channel).socket.setReusePort(reusePort); return this; } catch (IOException e) { throw new ChannelException(e); @@ -84,7 +84,7 @@ public KQueueServerSocketChannelConfig setReusePort(boolean reusePort) { public boolean isReusePort() { try { - return channel.socket.isReusePort(); + return ((KQueueServerSocketChannel) channel).socket.isReusePort(); } catch (IOException e) { throw new ChannelException(e); } @@ -92,7 +92,7 @@ public boolean isReusePort() { public KQueueServerSocketChannelConfig setAcceptFilter(AcceptFilter acceptFilter) { try { - channel.socket.setAcceptFilter(acceptFilter); + ((KQueueServerSocketChannel) channel).socket.setAcceptFilter(acceptFilter); return this; } catch (IOException e) { throw new ChannelException(e); @@ -101,7 +101,7 @@ public KQueueServerSocketChannelConfig setAcceptFilter(AcceptFilter acceptFilter public AcceptFilter getAcceptFilter() { try { - return channel.socket.getAcceptFilter(); + return ((KQueueServerSocketChannel) channel).socket.getAcceptFilter(); } catch (IOException e) { throw new ChannelException(e); } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueSocketChannelConfig.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueSocketChannelConfig.java index 8662e55c7b98..b5c718b9113a 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueSocketChannelConfig.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueSocketChannelConfig.java @@ -41,12 +41,10 @@ @UnstableApi public final class KQueueSocketChannelConfig extends KQueueChannelConfig implements SocketChannelConfig { - private final KQueueSocketChannel channel; private volatile boolean allowHalfClosure; KQueueSocketChannelConfig(KQueueSocketChannel channel) { super(channel); - this.channel = channel; if (PlatformDependent.canEnableTcpNoDelayByDefault()) { setTcpNoDelay(true); } @@ -131,7 +129,7 @@ public boolean setOption(ChannelOption option, T value) { @Override public int getReceiveBufferSize() { try { - return channel.socket.getReceiveBufferSize(); + return ((KQueueSocketChannel) channel).socket.getReceiveBufferSize(); } catch (IOException e) { throw new ChannelException(e); } @@ -140,7 +138,7 @@ public int getReceiveBufferSize() { @Override public int getSendBufferSize() { try { - return channel.socket.getSendBufferSize(); + return ((KQueueSocketChannel) channel).socket.getSendBufferSize(); } catch (IOException e) { throw new ChannelException(e); } @@ -149,7 +147,7 @@ public int getSendBufferSize() { @Override public int getSoLinger() { try { - return channel.socket.getSoLinger(); + return ((KQueueSocketChannel) channel).socket.getSoLinger(); } catch (IOException e) { throw new ChannelException(e); } @@ -158,7 +156,7 @@ public int getSoLinger() { @Override public int getTrafficClass() { try { - return channel.socket.getTrafficClass(); + return ((KQueueSocketChannel) channel).socket.getTrafficClass(); } catch (IOException e) { throw new ChannelException(e); } @@ -167,7 +165,7 @@ public int getTrafficClass() { @Override public boolean isKeepAlive() { try { - return channel.socket.isKeepAlive(); + return ((KQueueSocketChannel) channel).socket.isKeepAlive(); } catch (IOException e) { throw new ChannelException(e); } @@ -176,7 +174,7 @@ public boolean isKeepAlive() { @Override public boolean isReuseAddress() { try { - return channel.socket.isReuseAddress(); + return ((KQueueSocketChannel) channel).socket.isReuseAddress(); } catch (IOException e) { throw new ChannelException(e); } @@ -185,7 +183,7 @@ public boolean isReuseAddress() { @Override public boolean isTcpNoDelay() { try { - return channel.socket.isTcpNoDelay(); + return ((KQueueSocketChannel) channel).socket.isTcpNoDelay(); } catch (IOException e) { throw new ChannelException(e); } @@ -193,7 +191,7 @@ public boolean isTcpNoDelay() { public int getSndLowAt() { try { - return channel.socket.getSndLowAt(); + return ((KQueueSocketChannel) channel).socket.getSndLowAt(); } catch (IOException e) { throw new ChannelException(e); } @@ -201,7 +199,7 @@ public int getSndLowAt() { public void setSndLowAt(int sndLowAt) { try { - channel.socket.setSndLowAt(sndLowAt); + ((KQueueSocketChannel) channel).socket.setSndLowAt(sndLowAt); } catch (IOException e) { throw new ChannelException(e); } @@ -209,7 +207,7 @@ public void setSndLowAt(int sndLowAt) { public boolean isTcpNoPush() { try { - return channel.socket.isTcpNoPush(); + return ((KQueueSocketChannel) channel).socket.isTcpNoPush(); } catch (IOException e) { throw new ChannelException(e); } @@ -217,7 +215,7 @@ public boolean isTcpNoPush() { public void setTcpNoPush(boolean tcpNoPush) { try { - channel.socket.setTcpNoPush(tcpNoPush); + ((KQueueSocketChannel) channel).socket.setTcpNoPush(tcpNoPush); } catch (IOException e) { throw new ChannelException(e); } @@ -226,7 +224,7 @@ public void setTcpNoPush(boolean tcpNoPush) { @Override public KQueueSocketChannelConfig setKeepAlive(boolean keepAlive) { try { - channel.socket.setKeepAlive(keepAlive); + ((KQueueSocketChannel) channel).socket.setKeepAlive(keepAlive); return this; } catch (IOException e) { throw new ChannelException(e); @@ -236,7 +234,7 @@ public KQueueSocketChannelConfig setKeepAlive(boolean keepAlive) { @Override public KQueueSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) { try { - channel.socket.setReceiveBufferSize(receiveBufferSize); + ((KQueueSocketChannel) channel).socket.setReceiveBufferSize(receiveBufferSize); return this; } catch (IOException e) { throw new ChannelException(e); @@ -246,7 +244,7 @@ public KQueueSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) { @Override public KQueueSocketChannelConfig setReuseAddress(boolean reuseAddress) { try { - channel.socket.setReuseAddress(reuseAddress); + ((KQueueSocketChannel) channel).socket.setReuseAddress(reuseAddress); return this; } catch (IOException e) { throw new ChannelException(e); @@ -256,7 +254,7 @@ public KQueueSocketChannelConfig setReuseAddress(boolean reuseAddress) { @Override public KQueueSocketChannelConfig setSendBufferSize(int sendBufferSize) { try { - channel.socket.setSendBufferSize(sendBufferSize); + ((KQueueSocketChannel) channel).socket.setSendBufferSize(sendBufferSize); calculateMaxBytesPerGatheringWrite(); return this; } catch (IOException e) { @@ -267,7 +265,7 @@ public KQueueSocketChannelConfig setSendBufferSize(int sendBufferSize) { @Override public KQueueSocketChannelConfig setSoLinger(int soLinger) { try { - channel.socket.setSoLinger(soLinger); + ((KQueueSocketChannel) channel).socket.setSoLinger(soLinger); return this; } catch (IOException e) { throw new ChannelException(e); @@ -277,7 +275,7 @@ public KQueueSocketChannelConfig setSoLinger(int soLinger) { @Override public KQueueSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) { try { - channel.socket.setTcpNoDelay(tcpNoDelay); + ((KQueueSocketChannel) channel).socket.setTcpNoDelay(tcpNoDelay); return this; } catch (IOException e) { throw new ChannelException(e); @@ -287,7 +285,7 @@ public KQueueSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) { @Override public KQueueSocketChannelConfig setTrafficClass(int trafficClass) { try { - channel.socket.setTrafficClass(trafficClass); + ((KQueueSocketChannel) channel).socket.setTrafficClass(trafficClass); return this; } catch (IOException e) { throw new ChannelException(e); From a95b7a791e93192af417c9587626b2c28750fde6 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Fri, 28 Sep 2018 10:29:12 -0700 Subject: [PATCH 195/417] Notify http2 error handler before closeStreamLocal on HEADERS write failure (#8332) Motivation: When writing an HTTP/2 HEADERS with END_STREAM=1, the application expects the stream to be closed afterward. However, the write can fail locally due to HPACK encoder and similar. When that happens we need to make sure to issue a RST_STREAM otherwise the stream can be closed locally but orphaned remotely. The RST_STREAM is typically handled by Http2ConnectionHandler.onStreamError, which will only send a RST_STREAM if that stream still exists locally. There are two possible flows for trailers, one handled immediately and one going through the flow controller. Previously they behaved differently, with the immedate code calling the error handler after closing the stream. The immediate code also used a listener for calling closeStreamLocal while the flow controlled code did so immediately after the write. The two code paths also differed in their VoidChannelPromise handling, but both were broken. The immediate code path called unvoid() only if END_STREAM=1, however it could always potentially add a listener via notifyLifecycleManagerOnError(). And the flow controlled code path unvoided incorrectly, changing the promise completion behavior. It also passed the wrong promise to closeStreamLocal() in FlowControlledBase. Modifications: Move closeStreamLocal handling after calls to onError. This is the primary change. Now call closeStreamLocal immediately instead of when the future completes. This is the more likely correct behavior as it matches that of DATA frames. Fix all the VoidChannelPromise handling. Result: Http2ConnectionHandler.onStreamError sees the same state as the remote and issues a RST_STREAM, properly cleaning up the stream. --- .../http2/DefaultHttp2ConnectionEncoder.java | 28 +++++++++---------- .../DefaultHttp2ConnectionEncoderTest.java | 28 +++++++++++++++++++ 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java index d0c5944cb9f7..351f0e63840e 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java @@ -190,17 +190,10 @@ public ChannelFuture writeHeaders(final ChannelHandlerContext ctx, final int str // for this stream. Http2RemoteFlowController flowController = flowController(); if (!endOfStream || !flowController.hasFlowControlled(stream)) { + // The behavior here should mirror that in FlowControlledHeaders + + promise = promise.unvoid(); boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream); - if (endOfStream) { - final Http2Stream finalStream = stream; - final ChannelFutureListener closeStreamLocalListener = new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - lifecycleManager.closeStreamLocal(finalStream, future); - } - }; - promise = promise.unvoid().addListener(closeStreamLocalListener); - } ChannelFuture future = frameWriter.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endOfStream, promise); @@ -222,6 +215,13 @@ public void operationComplete(ChannelFuture future) throws Exception { lifecycleManager.onError(ctx, true, failureCause); } + if (endOfStream) { + // Must handle calling onError before calling closeStreamLocal, otherwise the error handler will + // incorrectly think the stream no longer exists and so may not send RST_STREAM or perform similar + // appropriate action. + lifecycleManager.closeStreamLocal(stream, future); + } + return future; } else { // Pass headers to the flow-controller so it can maintain their sequence relative to DATA frames. @@ -288,6 +288,7 @@ public ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId, i // Reserve the promised stream. connection.local().reservePushStream(promisedStreamId, stream); + promise = promise.unvoid(); ChannelFuture future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise); // Writing headers may fail during the encode state if they violate HPACK limits. @@ -468,7 +469,7 @@ private final class FlowControlledHeaders extends FlowControlledBase { FlowControlledHeaders(Http2Stream stream, Http2Headers headers, int streamDependency, short weight, boolean exclusive, int padding, boolean endOfStream, ChannelPromise promise) { - super(stream, padding, endOfStream, promise); + super(stream, padding, endOfStream, promise.unvoid()); this.headers = headers; this.streamDependency = streamDependency; this.weight = weight; @@ -491,9 +492,8 @@ public void error(ChannelHandlerContext ctx, Throwable cause) { @Override public void write(ChannelHandlerContext ctx, int allowedBytes) { boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream); - if (promise.isVoid()) { - promise = ctx.newPromise(); - } + // The code is currently requiring adding this listener before writing, in order to call onError() before + // closeStreamLocal(). promise.addListener(this); ChannelFuture f = frameWriter.writeHeaders(ctx, stream.id(), headers, streamDependency, weight, exclusive, diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java index cdbfaa4ebdb4..cf28944a77ed 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java @@ -34,6 +34,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; @@ -65,6 +66,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -707,6 +709,32 @@ public void headersWriteShouldHalfClosePushStream() throws Exception { verify(lifecycleManager).closeStreamLocal(eq(stream), eq(promise)); } + @Test + public void headersWriteShouldHalfCloseAfterOnError() throws Exception { + final ChannelPromise promise = newPromise(); + final Throwable ex = new RuntimeException(); + // Fake an encoding error, like HPACK's HeaderListSizeException + when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), + eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true), eq(promise))) + .thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocation) { + promise.setFailure(ex); + return promise; + } + }); + + writeAllFlowControlledFrames(); + createStream(STREAM_ID, false); + encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise); + + assertTrue(promise.isDone()); + assertFalse(promise.isSuccess()); + InOrder inOrder = inOrder(lifecycleManager); + inOrder.verify(lifecycleManager).onError(eq(ctx), eq(true), eq(ex)); + inOrder.verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(promise)); + } + @Test public void encoderDelegatesGoAwayToLifeCycleManager() { ChannelPromise promise = newPromise(); From 3a96e7373b55d97c1b1d789b2a78fa903af5b684 Mon Sep 17 00:00:00 2001 From: Matteo Merli Date: Fri, 28 Sep 2018 13:52:00 -0700 Subject: [PATCH 196/417] Added option to do busy-wait on epoll (#8267) Motivation: Add an option (through a SelectStrategy return code) to have the Netty event loop thread to do busy-wait on the epoll. The reason for this change is to avoid the context switch cost that comes when the event loop thread is blocked on the epoll_wait() call. On average, the context switch has a penalty of ~13usec. This benefits both: The latency when reading from a socket Scheduling tasks to be executed on the event loop thread. The tradeoff, when enabling this feature, is that the event loop thread will be using 100% cpu, even when inactive. Modification: Added SelectStrategy option to return BUSY_WAIT Epoll loop will do a epoll_wait() with no timeout Use pause instruction to hint to processor that we're in a busy loop Result: When enabled, minimizes impact of context switch in the critical path --- .../src/main/c/netty_epoll_native.c | 28 +++++ .../netty/channel/epoll/EpollEventLoop.java | 9 ++ .../java/io/netty/channel/epoll/Native.java | 15 +++ .../EpollETSocketStringEchoBusyWaitTest.java | 31 ++++++ .../EpollLTSocketStringEchoBusyWaitTest.java | 31 ++++++ .../EpollSocketStringEchoBusyWaitTest.java | 101 ++++++++++++++++++ .../netty/channel/kqueue/KQueueEventLoop.java | 4 + .../java/io/netty/channel/SelectStrategy.java | 4 + .../io/netty/channel/nio/NioEventLoop.java | 4 + 9 files changed, 227 insertions(+) create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollETSocketStringEchoBusyWaitTest.java create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollLTSocketStringEchoBusyWaitTest.java create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketStringEchoBusyWaitTest.java diff --git a/transport-native-epoll/src/main/c/netty_epoll_native.c b/transport-native-epoll/src/main/c/netty_epoll_native.c index 19a83724197a..96e55e1ecc42 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_native.c +++ b/transport-native-epoll/src/main/c/netty_epoll_native.c @@ -213,6 +213,33 @@ static jint netty_epoll_native_epollWait0(JNIEnv* env, jclass clazz, jint efd, j return -err; } +static inline void cpu_relax() { +#if defined(__x86_64__) + asm volatile("pause\n": : :"memory"); +#endif +} + +static jint netty_epoll_native_epollBusyWait0(JNIEnv* env, jclass clazz, jint efd, jlong address, jint len) { + struct epoll_event *ev = (struct epoll_event*) (intptr_t) address; + int result, err; + + // Zeros = poll (aka return immediately). + do { + result = epoll_wait(efd, ev, len, 0); + if (result == 0) { + // Since we're always polling epoll_wait with no timeout, + // signal CPU that we're in a busy loop + cpu_relax(); + } + + if (result >= 0) { + return result; + } + } while((err = errno) == EINTR); + + return -err; +} + static jint netty_epoll_native_epollCtlAdd0(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags) { int res = epollCtl(env, efd, EPOLL_CTL_ADD, fd, flags); if (res < 0) { @@ -387,6 +414,7 @@ static const JNINativeMethod fixed_method_table[] = { { "timerFdRead", "(I)V", (void *) netty_epoll_native_timerFdRead }, { "epollCreate", "()I", (void *) netty_epoll_native_epollCreate }, { "epollWait0", "(IJIIII)I", (void *) netty_epoll_native_epollWait0 }, + { "epollBusyWait0", "(IJI)I", (void *) netty_epoll_native_epollBusyWait0 }, { "epollCtlAdd0", "(III)I", (void *) netty_epoll_native_epollCtlAdd0 }, { "epollCtlMod0", "(III)I", (void *) netty_epoll_native_epollCtlMod0 }, { "epollCtlDel0", "(II)I", (void *) netty_epoll_native_epollCtlDel0 }, diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index 462b0b0a336a..a2707d96a997 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -257,6 +257,10 @@ private int epollWaitNow() throws IOException { return Native.epollWait(epollFd, events, timerFd, 0, 0); } + private int epollBusyWait() throws IOException { + return Native.epollBusyWait(epollFd, events); + } + @Override protected void run() { for (;;) { @@ -265,6 +269,11 @@ protected void run() { switch (strategy) { case SelectStrategy.CONTINUE: continue; + + case SelectStrategy.BUSY_WAIT: + strategy = epollBusyWait(); + break; + case SelectStrategy.SELECT: strategy = epollWait(WAKEN_UP_UPDATER.getAndSet(this, 0) == 1); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java index 7c501da80b22..1eb2e5d3ff38 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java @@ -120,6 +120,21 @@ public static int epollWait(FileDescriptor epollFd, EpollEventArray events, File } private static native int epollWait0(int efd, long address, int len, int timerFd, int timeoutSec, int timeoutNs); + /** + * Non-blocking variant of + * {@link #epollWait(FileDescriptor, EpollEventArray, FileDescriptor, int, int)} + * that will also hint to processor we are in a busy-wait loop. + */ + public static int epollBusyWait(FileDescriptor epollFd, EpollEventArray events) throws IOException { + int ready = epollBusyWait0(epollFd.intValue(), events.memoryAddress(), events.length()); + if (ready < 0) { + throw newIOException("epoll_wait", ready); + } + return ready; + } + + private static native int epollBusyWait0(int efd, long address, int len); + public static void epollCtlAdd(int efd, final int fd, final int flags) throws IOException { int res = epollCtlAdd0(efd, fd, flags); if (res < 0) { diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollETSocketStringEchoBusyWaitTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollETSocketStringEchoBusyWaitTest.java new file mode 100644 index 000000000000..e599e3bef881 --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollETSocketStringEchoBusyWaitTest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.epoll; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBufAllocator; + +public class EpollETSocketStringEchoBusyWaitTest extends EpollSocketStringEchoBusyWaitTest { + + @Override + protected void configure(ServerBootstrap bootstrap, Bootstrap bootstrap2, ByteBufAllocator allocator) { + super.configure(bootstrap, bootstrap2, allocator); + bootstrap.option(EpollChannelOption.EPOLL_MODE, EpollMode.EDGE_TRIGGERED) + .childOption(EpollChannelOption.EPOLL_MODE, EpollMode.EDGE_TRIGGERED); + bootstrap2.option(EpollChannelOption.EPOLL_MODE, EpollMode.EDGE_TRIGGERED); + } +} diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollLTSocketStringEchoBusyWaitTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollLTSocketStringEchoBusyWaitTest.java new file mode 100644 index 000000000000..d6098774c0ab --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollLTSocketStringEchoBusyWaitTest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.epoll; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBufAllocator; + +public class EpollLTSocketStringEchoBusyWaitTest extends EpollSocketStringEchoBusyWaitTest { + + @Override + protected void configure(ServerBootstrap bootstrap, Bootstrap bootstrap2, ByteBufAllocator allocator) { + super.configure(bootstrap, bootstrap2, allocator); + bootstrap.option(EpollChannelOption.EPOLL_MODE, EpollMode.LEVEL_TRIGGERED) + .childOption(EpollChannelOption.EPOLL_MODE, EpollMode.LEVEL_TRIGGERED); + bootstrap2.option(EpollChannelOption.EPOLL_MODE, EpollMode.LEVEL_TRIGGERED); + } +} diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketStringEchoBusyWaitTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketStringEchoBusyWaitTest.java new file mode 100644 index 000000000000..f67b6da94fa0 --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketStringEchoBusyWaitTest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2018 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: + * + * http://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.channel.epoll; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.AfterClass; +import org.junit.BeforeClass; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.SelectStrategy; +import io.netty.channel.SelectStrategyFactory; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.TestsuitePermutation.BootstrapComboFactory; +import io.netty.testsuite.transport.TestsuitePermutation.BootstrapFactory; +import io.netty.testsuite.transport.socket.SocketStringEchoTest; +import io.netty.util.IntSupplier; +import io.netty.util.concurrent.DefaultThreadFactory; + +public class EpollSocketStringEchoBusyWaitTest extends SocketStringEchoTest { + + private static EventLoopGroup EPOLL_LOOP; + + @BeforeClass + public static void setup() throws Exception { + EPOLL_LOOP = new EpollEventLoopGroup(2, new DefaultThreadFactory("testsuite-epoll-busy-wait", true), + new SelectStrategyFactory() { + @Override + public SelectStrategy newSelectStrategy() { + return new SelectStrategy() { + @Override + public int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) { + return SelectStrategy.BUSY_WAIT; + } + }; + } + }); + } + + @AfterClass + public static void teardown() throws Exception { + if (EPOLL_LOOP != null) { + EPOLL_LOOP.shutdownGracefully(); + } + } + + @Override + protected List> newFactories() { + List> list = + new ArrayList>(); + final BootstrapFactory sbf = serverSocket(); + final BootstrapFactory cbf = clientSocket(); + list.add(new BootstrapComboFactory() { + @Override + public ServerBootstrap newServerInstance() { + return sbf.newInstance(); + } + + @Override + public Bootstrap newClientInstance() { + return cbf.newInstance(); + } + }); + + return list; + } + + private static BootstrapFactory serverSocket() { + return new BootstrapFactory() { + @Override + public ServerBootstrap newInstance() { + return new ServerBootstrap().group(EPOLL_LOOP, EPOLL_LOOP).channel(EpollServerSocketChannel.class); + } + }; + } + + private static BootstrapFactory clientSocket() { + return new BootstrapFactory() { + @Override + public Bootstrap newInstance() { + return new Bootstrap().group(EPOLL_LOOP).channel(EpollSocketChannel.class); + } + }; + } +} diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java index 64a09c97d1bf..8f2a4ca45d53 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java @@ -208,6 +208,10 @@ protected void run() { switch (strategy) { case SelectStrategy.CONTINUE: continue; + + case SelectStrategy.BUSY_WAIT: + // fall-through to SELECT since the busy-wait is not supported with kqueue + case SelectStrategy.SELECT: strategy = kqueueWait(WAKEN_UP_UPDATER.getAndSet(this, 0) == 1); diff --git a/transport/src/main/java/io/netty/channel/SelectStrategy.java b/transport/src/main/java/io/netty/channel/SelectStrategy.java index 0c8aca5789a7..447fb7f3f100 100644 --- a/transport/src/main/java/io/netty/channel/SelectStrategy.java +++ b/transport/src/main/java/io/netty/channel/SelectStrategy.java @@ -33,6 +33,10 @@ public interface SelectStrategy { * Indicates the IO loop should be retried, no blocking select to follow directly. */ int CONTINUE = -2; + /** + * Indicates the IO loop to poll for new events without blocking. + */ + int BUSY_WAIT = -3; /** * The {@link SelectStrategy} can be used to steer the outcome of a potential select diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index a95d514c1964..c574dded6304 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -404,6 +404,10 @@ protected void run() { switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) { case SelectStrategy.CONTINUE: continue; + + case SelectStrategy.BUSY_WAIT: + // fall-through to SELECT since the busy-wait is not supported with NIO + case SelectStrategy.SELECT: select(wakenUp.getAndSet(false)); From 6cebb6069b3f30eedaddb12d2d22a6dd66277047 Mon Sep 17 00:00:00 2001 From: Dmitriy Dumanskiy Date: Fri, 5 Oct 2018 14:06:44 +0300 Subject: [PATCH 197/417] remove unnecessary vararg argument in PooledByteBufAllocator (#8338) Motivation: No need in varargs, the method always accepts array. Modification: ... replaced with [] --- .../src/main/java/io/netty/buffer/PooledByteBufAllocator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java index b613fea9e1cb..aa9ee28b9484 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java @@ -580,7 +580,7 @@ final long usedDirectMemory() { return usedMemory(directArenas); } - private static long usedMemory(PoolArena... arenas) { + private static long usedMemory(PoolArena[] arenas) { if (arenas == null) { return -1; } From fc28bccdf19ea9d145f274a87002bdb1f45f840c Mon Sep 17 00:00:00 2001 From: Jussi Virtanen Date: Tue, 9 Oct 2018 05:11:07 +0100 Subject: [PATCH 198/417] Fix SelectableChannel support in NioEventLoop (#8344) Motivation: Unless the 'io.netty.noKeySetOptimization' system property is set, registering a SelectableChannel instance to a NioEventLoop results in a ClassCastException: io.netty.channel.nio.SelectedSelectionKeySetSelector cannot be cast to java.nio.channels.spi.AbstractSelector Modifications: Instead of 'selector', pass 'unwrappedSelector' to SelectableChannel. Result: It is possible to register a SelectableChannel instance without setting the 'io.netty.noKeySetOptimization' system property. --- .../io/netty/channel/nio/NioEventLoop.java | 2 +- .../netty/channel/nio/NioEventLoopTest.java | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index c574dded6304..187e1ec1a0bb 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -294,7 +294,7 @@ public void register(final SelectableChannel ch, final int interestOps, final Ni } try { - ch.register(selector, interestOps, task); + ch.register(unwrappedSelector, interestOps, task); } catch (Exception e) { throw new EventLoopException("failed to register a channel", e); } diff --git a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java index 124e0fe8a04a..1d5801377eeb 100644 --- a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java +++ b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java @@ -24,7 +24,10 @@ import io.netty.util.concurrent.Future; import org.junit.Test; +import java.net.InetSocketAddress; +import java.nio.channels.SelectionKey; import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -132,4 +135,40 @@ public void run() { group.shutdownGracefully(); } } + + @Test(timeout = 1000) + public void testSelectableChannel() throws Exception { + NioEventLoopGroup group = new NioEventLoopGroup(1); + NioEventLoop loop = (NioEventLoop) group.next(); + + try { + Channel channel = new NioServerSocketChannel(); + loop.register(channel).syncUninterruptibly(); + channel.bind(new InetSocketAddress(0)).syncUninterruptibly(); + + SocketChannel selectableChannel = SocketChannel.open(); + selectableChannel.configureBlocking(false); + selectableChannel.connect(channel.localAddress()); + + final CountDownLatch latch = new CountDownLatch(1); + + loop.register(selectableChannel, SelectionKey.OP_CONNECT, new NioTask() { + @Override + public void channelReady(SocketChannel ch, SelectionKey key) { + latch.countDown(); + } + + @Override + public void channelUnregistered(SocketChannel ch, Throwable cause) { + } + }); + + latch.await(); + + selectableChannel.close(); + channel.close().syncUninterruptibly(); + } finally { + group.shutdownGracefully(); + } + } } From 83dc3b503ef692601a77f46c612c7e00bafd2336 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Thu, 11 Oct 2018 08:56:30 +0200 Subject: [PATCH 199/417] ByteBufInputStream is always allocating a StringBuilder instance (#8347) Motivation: Avoid creating any StringBuilder instance if ByteBufInputStream::readLine isn't used Modifications: The StringBuilder instance is lazy allocated on demand and are added new test case branches to address the increased complexity of ByteBufInputStream::readLine Result: Reduced GC activity if ByteBufInputStream::readLine isn't used --- .../io/netty/buffer/ByteBufInputStream.java | 25 ++++++++++++++++--- .../io/netty/buffer/ByteBufStreamTest.java | 6 +++-- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java b/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java index 2d8d34d32317..b7b67d54089b 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java @@ -16,6 +16,7 @@ package io.netty.buffer; import io.netty.util.ReferenceCounted; +import io.netty.util.internal.StringUtil; import java.io.DataInput; import java.io.DataInputStream; @@ -240,18 +241,23 @@ public int readInt() throws IOException { return buffer.readInt(); } - private final StringBuilder lineBuf = new StringBuilder(); + private StringBuilder lineBuf; @Override public String readLine() throws IOException { - lineBuf.setLength(0); + if (lineBuf != null) { + lineBuf.setLength(0); + } + + boolean anyChar = false; loop: while (true) { if (!buffer.isReadable()) { - return lineBuf.length() > 0 ? lineBuf.toString() : null; + return toStringIfAnyChar(lineBuf, anyChar); } int c = buffer.readUnsignedByte(); + anyChar = true; switch (c) { case '\n': break loop; @@ -263,11 +269,22 @@ public String readLine() throws IOException { break loop; default: + if (lineBuf == null) { + lineBuf = new StringBuilder(); + } lineBuf.append((char) c); } } - return lineBuf.toString(); + return lineBuf != null && lineBuf.length() > 0 ? lineBuf.toString() : StringUtil.EMPTY_STRING; + } + + private static String toStringIfAnyChar(StringBuilder lineBuf, boolean anyChars) { + if (anyChars) { + return lineBuf != null && lineBuf.length() > 0 ? lineBuf.toString() : StringUtil.EMPTY_STRING; + } else { + return null; + } } @Override diff --git a/buffer/src/test/java/io/netty/buffer/ByteBufStreamTest.java b/buffer/src/test/java/io/netty/buffer/ByteBufStreamTest.java index f05594d1c12e..222ddcf7ee73 100644 --- a/buffer/src/test/java/io/netty/buffer/ByteBufStreamTest.java +++ b/buffer/src/test/java/io/netty/buffer/ByteBufStreamTest.java @@ -188,11 +188,13 @@ public void testReadLine() throws Exception { String s = in.readLine(); assertNull(s); - int charCount = 5; //total chars in the string below without new line characters - byte[] abc = "a\nb\r\nc\nd\ne".getBytes(utf8); + int charCount = 7; //total chars in the string below without new line characters + byte[] abc = "\na\n\nb\r\nc\nd\ne".getBytes(utf8); buf.writeBytes(abc); in.mark(charCount); + assertEquals("", in.readLine()); assertEquals("a", in.readLine()); + assertEquals("", in.readLine()); assertEquals("b", in.readLine()); assertEquals("c", in.readLine()); assertEquals("d", in.readLine()); From 0e4186c5525eacf8fd1f3ce706c5908546c307ec Mon Sep 17 00:00:00 2001 From: Dmitriy Dumanskiy Date: Thu, 11 Oct 2018 09:59:47 +0300 Subject: [PATCH 200/417] deprecate IntegerHolder for removal (#8339) Motivation: Seems like IntegerHolder counterHashCode field is the very old legacy field that is no longer used. Should be marked as deprecated and removed in the future versions. Modification: IntegerHolder class, InternalThreadLocalMap.counterHashCode() and InternalThreadLocalMap.setCounterHashCode(IntegerHolder counterHashCode) are now deprecated. --- .../src/main/java/io/netty/util/internal/IntegerHolder.java | 4 ++++ .../java/io/netty/util/internal/InternalThreadLocalMap.java | 2 ++ 2 files changed, 6 insertions(+) diff --git a/common/src/main/java/io/netty/util/internal/IntegerHolder.java b/common/src/main/java/io/netty/util/internal/IntegerHolder.java index 2a8d069a2747..e19335ccf059 100644 --- a/common/src/main/java/io/netty/util/internal/IntegerHolder.java +++ b/common/src/main/java/io/netty/util/internal/IntegerHolder.java @@ -16,6 +16,10 @@ package io.netty.util.internal; +/** + * @deprecated For removal in netty 4.2 + */ +@Deprecated public final class IntegerHolder { public int value; } diff --git a/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java b/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java index 6ab3bdf1316d..0a6a6c564871 100644 --- a/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java +++ b/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java @@ -257,10 +257,12 @@ public Map, Map> typeParameterMatcherFind return cache; } + @Deprecated public IntegerHolder counterHashCode() { return counterHashCode; } + @Deprecated public void setCounterHashCode(IntegerHolder counterHashCode) { this.counterHashCode = counterHashCode; } From 652650b0dbbf515d8ddb6322fcd2bade91452869 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 11 Oct 2018 18:46:10 +0200 Subject: [PATCH 201/417] Correctly decrement pending bytes when submitting AbstractWriteTask fails. (#8349) Motivation: Currently we may end up in the situation that we incremented the pending bytes before submitting the AbstractWriteTask but never decrement these again if the submitting of the task fails. This may result in incorrect watermark handling. Modifications: - Correctly decrement pending bytes if subimitting of task fails and also ensure we recycle it correctly. - Add unit test. Result: Fixes https://github.com/netty/netty/issues/8343. --- .../AbstractChannelHandlerContext.java | 51 ++++++++--- .../channel/ChannelOutboundBufferTest.java | 85 +++++++++++++++++++ 2 files changed, 122 insertions(+), 14 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java index 3b155a9e2348..0a81bd6b0f50 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java @@ -816,13 +816,19 @@ private void write(Object msg, boolean flush, ChannelPromise promise) { next.invokeWrite(m, promise); } } else { - AbstractWriteTask task; + final AbstractWriteTask task; if (flush) { task = WriteAndFlushTask.newInstance(next, m, promise); } else { task = WriteTask.newInstance(next, m, promise); } - safeExecute(executor, task, promise, m); + if (!safeExecute(executor, task, promise, m)) { + // We failed to submit the AbstractWriteTask. We need to cancel it so we decrement the pending bytes + // and put it back in the Recycler for re-use later. + // + // See https://github.com/netty/netty/issues/8343. + task.cancel(); + } } } @@ -1002,9 +1008,10 @@ public boolean hasAttr(AttributeKey key) { return channel().hasAttr(key); } - private static void safeExecute(EventExecutor executor, Runnable runnable, ChannelPromise promise, Object msg) { + private static boolean safeExecute(EventExecutor executor, Runnable runnable, ChannelPromise promise, Object msg) { try { executor.execute(runnable); + return true; } catch (Throwable cause) { try { promise.setFailure(cause); @@ -1013,6 +1020,7 @@ private static void safeExecute(EventExecutor executor, Runnable runnable, Chann ReferenceCountUtil.release(msg); } } + return false; } } @@ -1063,20 +1071,35 @@ protected static void init(AbstractWriteTask task, AbstractChannelHandlerContext @Override public final void run() { try { - // Check for null as it may be set to null if the channel is closed already - if (ESTIMATE_TASK_SIZE_ON_SUBMIT) { - ctx.pipeline.decrementPendingOutboundBytes(size); - } + decrementPendingOutboundBytes(); write(ctx, msg, promise); } finally { - // Set to null so the GC can collect them directly - ctx = null; - msg = null; - promise = null; - handle.recycle(this); + recycle(); + } + } + + void cancel() { + try { + decrementPendingOutboundBytes(); + } finally { + recycle(); } } + private void decrementPendingOutboundBytes() { + if (ESTIMATE_TASK_SIZE_ON_SUBMIT) { + ctx.pipeline.decrementPendingOutboundBytes(size); + } + } + + private void recycle() { + // Set to null so the GC can collect them directly + ctx = null; + msg = null; + promise = null; + handle.recycle(this); + } + protected void write(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) { ctx.invokeWrite(msg, promise); } @@ -1091,7 +1114,7 @@ protected WriteTask newObject(Handle handle) { } }; - private static WriteTask newInstance( + static WriteTask newInstance( AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) { WriteTask task = RECYCLER.get(); init(task, ctx, msg, promise); @@ -1112,7 +1135,7 @@ protected WriteAndFlushTask newObject(Handle handle) { } }; - private static WriteAndFlushTask newInstance( + static WriteAndFlushTask newInstance( AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) { WriteAndFlushTask task = RECYCLER.get(); init(task, ctx, msg, promise); diff --git a/transport/src/test/java/io/netty/channel/ChannelOutboundBufferTest.java b/transport/src/test/java/io/netty/channel/ChannelOutboundBufferTest.java index 470249091680..955de75395da 100644 --- a/transport/src/test/java/io/netty/channel/ChannelOutboundBufferTest.java +++ b/transport/src/test/java/io/netty/channel/ChannelOutboundBufferTest.java @@ -19,10 +19,16 @@ import io.netty.buffer.CompositeByteBuf; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.CharsetUtil; +import io.netty.util.concurrent.DefaultThreadFactory; +import io.netty.util.concurrent.RejectedExecutionHandlers; +import io.netty.util.concurrent.SingleThreadEventExecutor; import org.junit.Test; import java.net.SocketAddress; import java.nio.ByteBuffer; +import java.util.Queue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.RejectedExecutionException; import static io.netty.buffer.Unpooled.*; import static org.hamcrest.Matchers.*; @@ -355,6 +361,85 @@ public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exceptio safeClose(ch); } + @Test(timeout = 5000) + public void testWriteTaskRejected() throws Exception { + final SingleThreadEventExecutor executor = new SingleThreadEventExecutor( + null, new DefaultThreadFactory("executorPool"), + true, 1, RejectedExecutionHandlers.reject()) { + @Override + protected void run() { + do { + Runnable task = takeTask(); + if (task != null) { + task.run(); + updateLastExecutionTime(); + } + } while (!confirmShutdown()); + } + + @Override + protected Queue newTaskQueue(int maxPendingTasks) { + return super.newTaskQueue(1); + } + }; + final CountDownLatch handlerAddedLatch = new CountDownLatch(1); + EmbeddedChannel ch = new EmbeddedChannel(); + ch.pipeline().addLast(executor, new ChannelOutboundHandlerAdapter() { + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + promise.setFailure(new AssertionError("Should not be called")); + } + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + handlerAddedLatch.countDown(); + } + }); + + // Lets wait until we are sure the handler was added. + handlerAddedLatch.await(); + + final CountDownLatch executeLatch = new CountDownLatch(1); + final CountDownLatch runLatch = new CountDownLatch(1); + executor.execute(new Runnable() { + @Override + public void run() { + try { + runLatch.countDown(); + executeLatch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + }); + + runLatch.await(); + + executor.execute(new Runnable() { + @Override + public void run() { + // Will not be executed but ensure the pending count is 1. + } + }); + + assertEquals(1, executor.pendingTasks()); + assertEquals(0, ch.unsafe().outboundBuffer().totalPendingWriteBytes()); + + ByteBuf buffer = buffer(128).writeZero(128); + ChannelFuture future = ch.write(buffer); + ch.runPendingTasks(); + + assertTrue(future.cause() instanceof RejectedExecutionException); + assertEquals(0, buffer.refCnt()); + + // In case of rejected task we should not have anything pending. + assertEquals(0, ch.unsafe().outboundBuffer().totalPendingWriteBytes()); + executeLatch.countDown(); + + safeClose(ch); + executor.shutdownGracefully(); + } + private static void safeClose(EmbeddedChannel ch) { ch.finish(); for (;;) { From 2a4bb346cfeda9caf1a17a242c562125e096bf3e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 12 Oct 2018 05:00:32 +0200 Subject: [PATCH 202/417] Correctly parse /etc/resolv.conf when contain multiple entries for searchdomain. (#8351) Motivation: ba594bcf4a62c47810f85c6d28e87367c6903ed4 added a utility to parse searchdomains defined in /etc/resolv.conf but did not correctly handle the case when multiple are defined that are seperated by either whitespace or tab. Modifications: - Correctly parse multiple entries - Add unit test. Result: Correctly parse multiple searchdomain entries. --- ...ixResolverDnsServerAddressStreamProvider.java | 8 ++++++-- ...solverDnsServerAddressStreamProviderTest.java | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java index 97a3a90d6b61..afe29313c41f 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java @@ -27,12 +27,12 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; import static io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.DNS_PORT; import static io.netty.util.internal.ObjectUtil.checkNotNull; @@ -59,6 +59,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ static final int DEFAULT_NDOTS = 1; private final DnsServerAddresses defaultNameServerAddresses; private final Map domainToNameServerStreamMap; + private static final Pattern SEARCH_DOMAIN_PATTERN = Pattern.compile("\\s+"); /** * Attempt to parse {@code /etc/resolv.conf} and files in the {@code /etc/resolver} directory by default. @@ -323,7 +324,10 @@ static List parseEtcResolverSearchDomains(File etcResolvConf) throws IOE } else if (line.startsWith(SEARCH_ROW_LABEL)) { int i = indexOfNonWhiteSpace(line, SEARCH_ROW_LABEL.length()); if (i >= 0) { - searchDomains.add(line.substring(i)); + // May contain more then one entry, either seperated by whitespace or tab. + // See https://linux.die.net/man/5/resolver + String[] domains = SEARCH_DOMAIN_PATTERN.split(line.substring(i)); + Collections.addAll(searchDomains, domains); } } } diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java index e428109412d6..33996679e631 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java @@ -139,6 +139,22 @@ public void searchDomainsWithMultipleSearch() throws IOException { assertEquals(Arrays.asList("linecorp.local", "squarecorp.local"), domains); } + @Test + public void searchDomainsWithMultipleSearchSeperatedByWhitespace() throws IOException { + File f = buildFile("search linecorp.local squarecorp.local\n" + + "nameserver 127.0.0.2\n"); + List domains = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(f); + assertEquals(Arrays.asList("linecorp.local", "squarecorp.local"), domains); + } + + @Test + public void searchDomainsWithMultipleSearchSeperatedByTab() throws IOException { + File f = buildFile("search linecorp.local\tsquarecorp.local\n" + + "nameserver 127.0.0.2\n"); + List domains = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(f); + assertEquals(Arrays.asList("linecorp.local", "squarecorp.local"), domains); + } + @Test public void searchDomainsPrecedence() throws IOException { File f = buildFile("domain linecorp.local\n" + From 5b3b8db07fdb66d05e1a44193ca7bcaa64420051 Mon Sep 17 00:00:00 2001 From: Johno Crawford Date: Fri, 12 Oct 2018 05:02:41 +0200 Subject: [PATCH 203/417] epoll_wait produces an EINVAL error since 4.1.30 (#8350) Motivation: epoll_wait should work in 4.1.30 like it did in 4.1.29. Modifications: Revert Integer.MAX_VALUE back to MAX_SCHEDULED_TIMERFD_NS (999,999,999). Add unit test. Result: epoll_wait will no longer throw EINVAL. --- .../netty/channel/epoll/EpollEventLoop.java | 11 ++++-- .../channel/epoll/EpollEventLoopTest.java | 38 ++++++++++++++----- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index a2707d96a997..33adf8627963 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -35,7 +35,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Queue; -import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; @@ -44,7 +43,7 @@ /** * {@link EventLoop} which uses epoll under the covers. Only works on Linux! */ -final class EpollEventLoop extends SingleThreadEventLoop { +class EpollEventLoop extends SingleThreadEventLoop { private static final InternalLogger logger = InternalLoggerFactory.getInstance(EpollEventLoop.class); private static final AtomicIntegerFieldUpdater WAKEN_UP_UPDATER = AtomicIntegerFieldUpdater.newUpdater(EpollEventLoop.class, "wakenUp"); @@ -75,6 +74,7 @@ public int get() throws Exception { return epollWaitNow(); } }; + @SuppressWarnings("unused") // AtomicIntegerFieldUpdater private volatile int wakenUp; private volatile int ioRatio = 50; @@ -248,7 +248,7 @@ private int epollWait(boolean oldWakeup) throws IOException { long totalDelay = delayNanos(System.nanoTime()); prevDeadlineNanos = curDeadlineNanos; delaySeconds = (int) min(totalDelay / 1000000000L, Integer.MAX_VALUE); - delayNanos = (int) min(totalDelay - delaySeconds * 1000000000L, Integer.MAX_VALUE); + delayNanos = (int) min(totalDelay - delaySeconds * 1000000000L, MAX_SCHEDULED_TIMERFD_NS); } return Native.epollWait(epollFd, events, timerFd, delaySeconds, delayNanos); } @@ -356,7 +356,10 @@ protected void run() { } } - private static void handleLoopException(Throwable t) { + /** + * Visible only for testing! + */ + void handleLoopException(Throwable t) { logger.warn("Unexpected exception in the selector loop.", t); // Prevent possible consecutive immediate failures that lead to diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollEventLoopTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollEventLoopTest.java index ebf529f73283..4e51114422bc 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollEventLoopTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollEventLoopTest.java @@ -15,32 +15,52 @@ */ package io.netty.channel.epoll; +import io.netty.channel.DefaultSelectStrategyFactory; import io.netty.channel.EventLoop; import io.netty.channel.EventLoopGroup; +import io.netty.util.concurrent.DefaultThreadFactory; import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.RejectedExecutionHandlers; +import io.netty.util.concurrent.ThreadPerTaskExecutor; import org.junit.Test; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class EpollEventLoopTest { @Test public void testScheduleBigDelayNotOverflow() { - EventLoopGroup group = new EpollEventLoopGroup(1); + final AtomicReference capture = new AtomicReference(); - final EventLoop el = group.next(); - Future future = el.schedule(new Runnable() { + final EventLoopGroup group = new EpollEventLoop(null, + new ThreadPerTaskExecutor(new DefaultThreadFactory(getClass())), 0, + DefaultSelectStrategyFactory.INSTANCE.newSelectStrategy(), RejectedExecutionHandlers.reject()) { @Override - public void run() { - // NOOP + void handleLoopException(Throwable t) { + capture.set(t); + super.handleLoopException(t); } - }, Long.MAX_VALUE, TimeUnit.MILLISECONDS); + }; - assertFalse(future.awaitUninterruptibly(1000)); - assertTrue(future.cancel(true)); - group.shutdownGracefully(); + try { + final EventLoop eventLoop = group.next(); + Future future = eventLoop.schedule(new Runnable() { + @Override + public void run() { + // NOOP + } + }, Long.MAX_VALUE, TimeUnit.MILLISECONDS); + + assertFalse(future.awaitUninterruptibly(1000)); + assertTrue(future.cancel(true)); + assertNull(capture.get()); + } finally { + group.shutdownGracefully(); + } } } From aae7cdca96130a22a3eeb77c69c4846e71facfc6 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 12 Oct 2018 09:27:46 +0200 Subject: [PATCH 204/417] Prevent NPE when attempting to set client key material with no alias (#8378) Motivation: It is possible that a client is unable to locate a certificate alias given the list of issuers and key types. In this case the X509KeyManager will return a null which when past to the OpenSslKeyMaterialProvider implementation may produce a NPE. If no matching alias could be found we should not call OpenSslKeyMaterialProvider at all which is also consistent what OpenJDK does. Modifications: - Add null check before calling OpenSslKeyMaterialProvider - Add unit test. Result: No more NPE caused by passing null as client alias. --- .../ssl/OpenSslKeyMaterialManager.java | 7 +- .../ssl/OpenSslKeyMaterialManagerTest.java | 83 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialManagerTest.java diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java index 8b8a41d1146b..94398d5cf204 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java @@ -83,7 +83,12 @@ void setKeyMaterialServerSide(ReferenceCountedOpenSslEngine engine) throws SSLEx void setKeyMaterialClientSide(ReferenceCountedOpenSslEngine engine, String[] keyTypes, X500Principal[] issuer) throws SSLException { String alias = chooseClientAlias(engine, keyTypes, issuer); - setKeyMaterial(engine, alias); + // Only try to set the keymaterial if we have a match. This is also consistent with what OpenJDK does: + // http://hg.openjdk.java.net/jdk/jdk11/file/76072a077ee1/ + // src/java.base/share/classes/sun/security/ssl/CertificateRequest.java#l362 + if (alias != null) { + setKeyMaterial(engine, alias); + } } private void setKeyMaterial(ReferenceCountedOpenSslEngine engine, String alias) throws SSLException { diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialManagerTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialManagerTest.java new file mode 100644 index 000000000000..ae197a23e98b --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialManagerTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.util.internal.EmptyArrays; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Test; + +import javax.net.ssl.SSLException; +import javax.net.ssl.X509ExtendedKeyManager; +import java.net.Socket; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +public class OpenSslKeyMaterialManagerTest { + + @Test + public void testChooseClientAliasReturnsNull() throws SSLException { + Assume.assumeTrue(OpenSsl.isAvailable()); + + X509ExtendedKeyManager keyManager = new X509ExtendedKeyManager() { + @Override + public String[] getClientAliases(String s, Principal[] principals) { + return EmptyArrays.EMPTY_STRINGS; + } + + @Override + public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) { + return null; + } + + @Override + public String[] getServerAliases(String s, Principal[] principals) { + return EmptyArrays.EMPTY_STRINGS; + } + + @Override + public String chooseServerAlias(String s, Principal[] principals, Socket socket) { + return null; + } + + @Override + public X509Certificate[] getCertificateChain(String s) { + return EmptyArrays.EMPTY_X509_CERTIFICATES; + } + + @Override + public PrivateKey getPrivateKey(String s) { + return null; + } + }; + + OpenSslKeyMaterialManager manager = new OpenSslKeyMaterialManager( + new OpenSslKeyMaterialProvider(keyManager, null) { + @Override + OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception { + Assert.fail("Should not be called when alias is null"); + return null; + } + }); + SslContext context = SslContextBuilder.forClient().sslProvider(SslProvider.OPENSSL).build(); + OpenSslEngine engine = + (OpenSslEngine) context.newEngine(UnpooledByteBufAllocator.DEFAULT); + manager.setKeyMaterialClientSide(engine, EmptyArrays.EMPTY_STRINGS, null); + } +} From adb4ce1f3135e59ca98a3173a1dacb755b18c5a5 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 13 Oct 2018 04:25:56 +0200 Subject: [PATCH 205/417] Use MACOSX_DEPLOYMENT_TARGET=10.6 when compile native transport on MacOS (#8379) Motivation: MACOSX_DEPLOYMENT_TARGET=10.6 needs to be used as everything before is not supported in 10.14 anymore. 10.6 was released 2009 so this should be a safe thing to do. Modifications: Use MACOSX_DEPLOYMENT_TARGET=10.6 Result: Be able to compile on MacOS 10.14 --- transport-native-kqueue/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 3e3ca9e23df0..a42bb0550af7 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -80,10 +80,10 @@ ${jni.compiler.args.ldflags} ${jni.compiler.args.cflags} - - MACOSX_DEPLOYMENT_TARGET=10.2 + MACOSX_DEPLOYMENT_TARGET=10.6 From 7062ceedb0ed0d781bdad0002057abcf50b0c92d Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Fri, 12 Oct 2018 21:24:40 -0700 Subject: [PATCH 206/417] Simplify ByteBufInputStream.readLine() logic (#8380) Motivation: While looking at the nice optimization done in https://github.com/netty/netty/pull/8347 I couldn't help noticing the logic could be simplified further. Apologies if this is just my OCD and inappropriate! Modifications: Reduce amount of code used for ByteBufInputStream.readLine() Result: Slightly smaller and simpler code --- .../io/netty/buffer/ByteBufInputStream.java | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java b/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java index b7b67d54089b..038cd8db459b 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java @@ -245,19 +245,15 @@ public int readInt() throws IOException { @Override public String readLine() throws IOException { + if (!buffer.isReadable()) { + return null; + } if (lineBuf != null) { lineBuf.setLength(0); } - boolean anyChar = false; - - loop: while (true) { - if (!buffer.isReadable()) { - return toStringIfAnyChar(lineBuf, anyChar); - } - + loop: do { int c = buffer.readUnsignedByte(); - anyChar = true; switch (c) { case '\n': break loop; @@ -274,19 +270,11 @@ public String readLine() throws IOException { } lineBuf.append((char) c); } - } + } while (buffer.isReadable()); return lineBuf != null && lineBuf.length() > 0 ? lineBuf.toString() : StringUtil.EMPTY_STRING; } - private static String toStringIfAnyChar(StringBuilder lineBuf, boolean anyChars) { - if (anyChars) { - return lineBuf != null && lineBuf.length() > 0 ? lineBuf.toString() : StringUtil.EMPTY_STRING; - } else { - return null; - } - } - @Override public long readLong() throws IOException { checkAvailable(8); From 4a2d03542b7d6c6da70b4ae7255717c6530621ff Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 14 Oct 2018 13:20:18 +0200 Subject: [PATCH 207/417] Exclude mainframer related files from git --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index a3f28acc926c..ed43883f5ba7 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,7 @@ hs_err_pid*.log dependency-reduced-pom.xml */.unison.* + +# exclude mainframer files +mainframer +.mainframer From a542d4d78bb5008a2730dc0958ccbb4c32d7813a Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 15 Oct 2018 15:10:22 +0200 Subject: [PATCH 208/417] Increase test timeout (#8385) Motivation: It has shown that the used test timeout may be too low when the CI is busy. Modifications: Increase timeout to 3 seconds. Result: Less false-positives. --- .../src/test/java/io/netty/channel/nio/NioEventLoopTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java index 1d5801377eeb..d3412c2d9963 100644 --- a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java +++ b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java @@ -136,7 +136,7 @@ public void run() { } } - @Test(timeout = 1000) + @Test(timeout = 3000) public void testSelectableChannel() throws Exception { NioEventLoopGroup group = new NioEventLoopGroup(1); NioEventLoop loop = (NioEventLoop) group.next(); From b59336142f143a5520c3d4948868be3ef57c0ae2 Mon Sep 17 00:00:00 2001 From: Dmitriy Dumanskiy Date: Mon, 15 Oct 2018 20:36:05 +0300 Subject: [PATCH 209/417] deprecate own ConcurrentSet for removal (#8340) Motivation: Java since version 6 has the wrapper for the ConcurrentHashMap that could be created via Collections.newSetFromMap(map). So no need to create own ConcurrentSet class. Also, since netty plans to switch to Java 8 soon there is another method for that - ConcurrentHashMap.newKeySet(). For now, marking this class @deprecated would be enough, just to warn users who use netty's ConcurrentSet. After switching to Java 8 ConcurrentSet should be removed and replaced with ConcurrentHashMap.newKeySet(). Modification: ConcurrentSet deprecated. --- .../src/main/java/io/netty/util/internal/ConcurrentSet.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/src/main/java/io/netty/util/internal/ConcurrentSet.java b/common/src/main/java/io/netty/util/internal/ConcurrentSet.java index 52f8c124d4ee..a735f4e430b6 100644 --- a/common/src/main/java/io/netty/util/internal/ConcurrentSet.java +++ b/common/src/main/java/io/netty/util/internal/ConcurrentSet.java @@ -20,6 +20,10 @@ import java.util.Iterator; import java.util.concurrent.ConcurrentMap; +/** + * @deprecated For removal in Netty 4.2. Please use {@link ConcurrentHashMap#newKeySet()} instead + */ +@Deprecated public final class ConcurrentSet extends AbstractSet implements Serializable { private static final long serialVersionUID = -6761513279741915432L; From 04001fdad1ca3c72625cddb1b1c7789381cb5f30 Mon Sep 17 00:00:00 2001 From: Julien Hoarau Date: Tue, 16 Oct 2018 16:05:45 +1100 Subject: [PATCH 210/417] Upgrade h2spec-maven-plugin (#8386) Motivation: - Version 0.3 would sometimes fail to report failing tests - New version contains upgraded version of h2spec Modifications: - Bump h2spec-maven-plugin to 0.6 - Remove excluded specs that are no passing - Add failing spec "half closed (remote): Sends a HEADERS frame" to exclude list Result: Build will fail when non excluded specs fails. --- testsuite-http2/pom.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 1dc4edd02f42..53bb65d2dec2 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -61,17 +61,15 @@ com.github.madgnome h2spec-maven-plugin - 0.3 + 0.6 io.netty.testsuite.http2.Http2Server - 3.8 - Sends a GOAWAY frame 4.2 - Sends a dynamic table size update at the end of header block 5.1 - idle: Sends a DATA frame - 5.1 - closed: Sends a DATA frame + 5.1 - half closed (remote): Sends a HEADERS frame 5.1 - closed: Sends a HEADERS frame 5.1.1 - Sends stream identifier that is numerically smaller than previous - 7 - Sends a GOAWAY frame with unknown error code 8.1.2.2 - Sends a HEADERS frame that contains the connection-specific header field 8.1.2.2 - Sends a HEADERS frame that contains the TE header field with any value other than "trailers" 8.1.2.3 - Sends a HEADERS frame with empty ":path" pseudo-header field From 9eebe7ed742e4ebeca17913782f327412babcf38 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Wed, 17 Oct 2018 08:28:39 +0200 Subject: [PATCH 211/417] Add full JdkSslContext public constructor, close #8384 (#8389) Motivation: JdkSslContext provides public constructors to wrap an existing `javax.net.ssl.SSLContext`. Sadly, some options combinations are not possible with the existing constructors, eg: * protocols is not exposed and always forced to null, so default protocols are always enforced * startTls is not exposed and always forced to false Modification: Add full constructor that take protocols and startTls parameters. Result: It's possible to create a JdkSslContext from an existing SSLContext and still have control over protocols and startTls --- .../io/netty/handler/ssl/JdkSslContext.java | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java index 2b61391b4916..6aef52a246ff 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java @@ -140,7 +140,10 @@ public class JdkSslContext extends SslContext { * @param sslContext the {@link SSLContext} to use. * @param isClient {@code true} if this context should create {@link SSLEngine}s for client-side usage. * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}. + * @deprecated Use {@link #JdkSslContext(SSLContext, boolean, Iterable, CipherSuiteFilter, + * ApplicationProtocolConfig, ClientAuth, String[], boolean)} */ + @Deprecated public JdkSslContext(SSLContext sslContext, boolean isClient, ClientAuth clientAuth) { this(sslContext, isClient, null, IdentityCipherSuiteFilter.INSTANCE, @@ -156,11 +159,44 @@ public JdkSslContext(SSLContext sslContext, boolean isClient, * @param cipherFilter the filter to use. * @param apn the {@link ApplicationProtocolConfig} to use. * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}. + * @deprecated Use {@link #JdkSslContext(SSLContext, boolean, Iterable, CipherSuiteFilter, + * ApplicationProtocolConfig, ClientAuth, String[], boolean)} */ + @Deprecated public JdkSslContext(SSLContext sslContext, boolean isClient, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, ClientAuth clientAuth) { - this(sslContext, isClient, ciphers, cipherFilter, toNegotiator(apn, !isClient), clientAuth, null, false); + this(sslContext, isClient, ciphers, cipherFilter, apn, clientAuth, null, false); + } + + /** + * Creates a new {@link JdkSslContext} from a pre-configured {@link SSLContext}. + * + * @param sslContext the {@link SSLContext} to use. + * @param isClient {@code true} if this context should create {@link SSLEngine}s for client-side usage. + * @param ciphers the ciphers to use or {@code null} if the standard should be used. + * @param cipherFilter the filter to use. + * @param apn the {@link ApplicationProtocolConfig} to use. + * @param clientAuth the {@link ClientAuth} to use. This will only be used when {@param isClient} is {@code false}. + * @param protocols the protocols to enable, or {@code null} to enable the default protocols. + * @param startTls {@code true} if the first write request shouldn't be encrypted + */ + public JdkSslContext(SSLContext sslContext, + boolean isClient, + Iterable ciphers, + CipherSuiteFilter cipherFilter, + ApplicationProtocolConfig apn, + ClientAuth clientAuth, + String[] protocols, + boolean startTls) { + this(sslContext, + isClient, + ciphers, + cipherFilter, + toNegotiator(apn, !isClient), + clientAuth, + protocols == null ? null : protocols.clone(), + startTls); } @SuppressWarnings("deprecation") From 0ddc62cec0b4715ae37cef0e6a9f8c79d42d74e9 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 17 Oct 2018 08:35:35 +0200 Subject: [PATCH 212/417] Add support for TLSv1.3 (#8293) Motivation: TLSv1.3 support is included in java11 and is also supported by OpenSSL 1.1.1, so we should support when possible. Modifications: - Add support for TLSv1.3 using either the JDK implementation or the native implementation provided by netty-tcnative when compiled against openssl 1.1.1 - Adjust unit tests for semantics provided by TLSv1.3 - Correctly handle custom Provider implementations that not support TLSv1.3 Result: Be able to use TLSv1.3 with netty. --- .../handler/ssl/CipherSuiteConverter.java | 69 ++-- .../io/netty/handler/ssl/JdkSslContext.java | 110 +++++- .../java/io/netty/handler/ssl/OpenSsl.java | 51 ++- .../ReferenceCountedOpenSslClientContext.java | 12 +- .../ssl/ReferenceCountedOpenSslContext.java | 82 +++-- .../ssl/ReferenceCountedOpenSslEngine.java | 76 ++-- .../java/io/netty/handler/ssl/SslUtils.java | 56 ++- .../handler/ssl/CipherSuiteCanaryTest.java | 4 + .../handler/ssl/CipherSuiteConverterTest.java | 3 +- .../ssl/ConscryptJdkSslEngineInteropTest.java | 12 +- .../handler/ssl/ConscryptSslEngineTest.java | 12 +- .../ssl/JdkConscryptSslEngineInteropTest.java | 13 +- .../ssl/JdkOpenSslEngineInteroptTest.java | 17 +- .../netty/handler/ssl/JdkSslEngineTest.java | 30 +- .../netty/handler/ssl/OpenSslEngineTest.java | 156 +++++--- .../ssl/OpenSslJdkSslEngineInteroptTest.java | 17 +- .../netty/handler/ssl/OpenSslTestUtils.java | 19 + .../ssl/ParameterizedSslHandlerTest.java | 17 +- .../ReferenceCountedOpenSslEngineTest.java | 12 +- .../io/netty/handler/ssl/SSLEngineTest.java | 340 +++++++++++++----- .../handler/ssl/SslContextBuilderTest.java | 20 +- .../io/netty/handler/ssl/SslErrorTest.java | 13 +- .../io/netty/handler/ssl/SslUtilsTest.java | 12 + pom.xml | 2 +- .../SocketSslClientRenegotiateTest.java | 6 +- .../transport/socket/SocketSslEchoTest.java | 26 +- .../socket/SocketSslSessionReuseTest.java | 2 +- 27 files changed, 862 insertions(+), 327 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java b/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java index 1fac36c1bcaa..deeeb0cfeb38 100644 --- a/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java +++ b/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java @@ -122,33 +122,6 @@ static boolean isO2JCached(String key, String protocol, String value) { } } - /** - * Converts the specified Java cipher suites to the colon-separated OpenSSL cipher suite specification. - */ - static String toOpenSsl(Iterable javaCipherSuites) { - final StringBuilder buf = new StringBuilder(); - for (String c: javaCipherSuites) { - if (c == null) { - break; - } - - String converted = toOpenSsl(c); - if (converted != null) { - c = converted; - } - - buf.append(c); - buf.append(':'); - } - - if (buf.length() > 0) { - buf.setLength(buf.length() - 1); - return buf.toString(); - } else { - return ""; - } - } - /** * Converts the specified Java cipher suite to its corresponding OpenSSL cipher suite name. * @@ -423,5 +396,47 @@ private static String toJavaHmacAlgo(String hmacAlgo) { return hmacAlgo; } + /** + * Convert the given ciphers if needed to OpenSSL format and append them to the correct {@link StringBuilder} + * depending on if its a TLSv1.3 cipher or not. If this methods returns without throwing an exception its + * guaranteed that at least one of the {@link StringBuilder}s contain some ciphers that can be used to configure + * OpenSSL. + */ + static void convertToCipherStrings( + Iterable cipherSuites, StringBuilder cipherBuilder, StringBuilder cipherTLSv13Builder) { + for (String c: cipherSuites) { + if (c == null) { + break; + } + + String converted = toOpenSsl(c); + if (converted == null) { + converted = c; + } + + if (!OpenSsl.isCipherSuiteAvailable(converted)) { + throw new IllegalArgumentException("unsupported cipher suite: " + c + '(' + converted + ')'); + } + + if (SslUtils.isTLSv13Cipher(converted)) { + cipherTLSv13Builder.append(converted); + cipherTLSv13Builder.append(':'); + } else { + cipherBuilder.append(converted); + cipherBuilder.append(':'); + } + } + + if (cipherBuilder.length() == 0 && cipherTLSv13Builder.length() == 0) { + throw new IllegalArgumentException("empty cipher suites"); + } + if (cipherBuilder.length() > 0) { + cipherBuilder.setLength(cipherBuilder.length() - 1); + } + if (cipherTLSv13Builder.length() > 0) { + cipherTLSv13Builder.setLength(cipherTLSv13Builder.length() - 1); + } + } + private CipherSuiteConverter() { } } diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java index 6aef52a246ff..d74bbdec981f 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java @@ -17,6 +17,7 @@ package io.netty.handler.ssl; import io.netty.buffer.ByteBufAllocator; +import io.netty.util.ReferenceCountUtil; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -26,6 +27,7 @@ import java.security.KeyException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.Provider; import java.security.Security; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; @@ -34,6 +36,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -58,11 +61,13 @@ public class JdkSslContext extends SslContext { static final String PROTOCOL = "TLS"; private static final String[] DEFAULT_PROTOCOLS; private static final List DEFAULT_CIPHERS; + private static final List DEFAULT_CIPHERS_NON_TLSV13; private static final Set SUPPORTED_CIPHERS; + private static final Set SUPPORTED_CIPHERS_NON_TLSV13; + private static final Provider DEFAULT_PROVIDER; static { SSLContext context; - int i; try { context = SSLContext.getInstance(PROTOCOL); context.init(null, null, null); @@ -70,31 +75,54 @@ public class JdkSslContext extends SslContext { throw new Error("failed to initialize the default SSL context", e); } + DEFAULT_PROVIDER = context.getProvider(); + SSLEngine engine = context.createSSLEngine(); + DEFAULT_PROTOCOLS = defaultProtocols(engine); + + SUPPORTED_CIPHERS = Collections.unmodifiableSet(supportedCiphers(engine)); + DEFAULT_CIPHERS = Collections.unmodifiableList(defaultCiphers(engine, SUPPORTED_CIPHERS)); + + List ciphersNonTLSv13 = new ArrayList(DEFAULT_CIPHERS); + ciphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES)); + DEFAULT_CIPHERS_NON_TLSV13 = Collections.unmodifiableList(ciphersNonTLSv13); + + Set suppertedCiphersNonTLSv13 = new LinkedHashSet(SUPPORTED_CIPHERS); + suppertedCiphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES)); + SUPPORTED_CIPHERS_NON_TLSV13 = Collections.unmodifiableSet(suppertedCiphersNonTLSv13); + + if (logger.isDebugEnabled()) { + logger.debug("Default protocols (JDK): {} ", Arrays.asList(DEFAULT_PROTOCOLS)); + logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS); + } + } + private static String[] defaultProtocols(SSLEngine engine) { // Choose the sensible default list of protocols. final String[] supportedProtocols = engine.getSupportedProtocols(); Set supportedProtocolsSet = new HashSet(supportedProtocols.length); - for (i = 0; i < supportedProtocols.length; ++i) { + for (int i = 0; i < supportedProtocols.length; ++i) { supportedProtocolsSet.add(supportedProtocols[i]); } List protocols = new ArrayList(); addIfSupported( supportedProtocolsSet, protocols, - "TLSv1.2", "TLSv1.1", "TLSv1"); + // Do not include TLSv1.3 for now by default. + SslUtils.PROTOCOL_TLS_V1_2, SslUtils.PROTOCOL_TLS_V1_1, SslUtils.PROTOCOL_TLS_V1); if (!protocols.isEmpty()) { - DEFAULT_PROTOCOLS = protocols.toArray(new String[0]); - } else { - DEFAULT_PROTOCOLS = engine.getEnabledProtocols(); + return protocols.toArray(new String[0]); } + return engine.getEnabledProtocols(); + } + private static Set supportedCiphers(SSLEngine engine) { // Choose the sensible default list of cipher suites. final String[] supportedCiphers = engine.getSupportedCipherSuites(); - SUPPORTED_CIPHERS = new HashSet(supportedCiphers.length); - for (i = 0; i < supportedCiphers.length; ++i) { + Set supportedCiphersSet = new LinkedHashSet(supportedCiphers.length); + for (int i = 0; i < supportedCiphers.length; ++i) { String supportedCipher = supportedCiphers[i]; - SUPPORTED_CIPHERS.add(supportedCipher); + supportedCiphersSet.add(supportedCipher); // IBM's J9 JVM utilizes a custom naming scheme for ciphers and only returns ciphers with the "SSL_" // prefix instead of the "TLS_" prefix (as defined in the JSSE cipher suite names [1]). According to IBM's // documentation [2] the "SSL_" prefix is "interchangeable" with the "TLS_" prefix. @@ -108,21 +136,29 @@ public class JdkSslContext extends SslContext { final String tlsPrefixedCipherName = "TLS_" + supportedCipher.substring("SSL_".length()); try { engine.setEnabledCipherSuites(new String[]{tlsPrefixedCipherName}); - SUPPORTED_CIPHERS.add(tlsPrefixedCipherName); + supportedCiphersSet.add(tlsPrefixedCipherName); } catch (IllegalArgumentException ignored) { // The cipher is not supported ... move on to the next cipher. } } } + return supportedCiphersSet; + } + + private static List defaultCiphers(SSLEngine engine, Set supportedCiphers) { List ciphers = new ArrayList(); - addIfSupported(SUPPORTED_CIPHERS, ciphers, DEFAULT_CIPHER_SUITES); + addIfSupported(supportedCiphers, ciphers, DEFAULT_CIPHER_SUITES); useFallbackCiphersIfDefaultIsEmpty(ciphers, engine.getEnabledCipherSuites()); - DEFAULT_CIPHERS = Collections.unmodifiableList(ciphers); + return ciphers; + } - if (logger.isDebugEnabled()) { - logger.debug("Default protocols (JDK): {} ", Arrays.asList(DEFAULT_PROTOCOLS)); - logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS); + private static boolean isTlsV13Supported(String[] protocols) { + for (String protocol: protocols) { + if (SslUtils.PROTOCOL_TLS_V1_3.equals(protocol)) { + return true; + } } + return false; } private final String[] protocols; @@ -205,11 +241,49 @@ public JdkSslContext(SSLContext sslContext, super(startTls); this.apn = checkNotNull(apn, "apn"); this.clientAuth = checkNotNull(clientAuth, "clientAuth"); + this.sslContext = checkNotNull(sslContext, "sslContext"); + + final List defaultCiphers; + final Set supportedCiphers; + if (DEFAULT_PROVIDER.equals(sslContext.getProvider())) { + this.protocols = protocols == null? DEFAULT_PROTOCOLS : protocols; + if (isTlsV13Supported(this.protocols)) { + supportedCiphers = SUPPORTED_CIPHERS; + defaultCiphers = DEFAULT_CIPHERS; + } else { + // TLSv1.3 is not supported, ensure we do not include any TLSv1.3 ciphersuite. + supportedCiphers = SUPPORTED_CIPHERS_NON_TLSV13; + defaultCiphers = DEFAULT_CIPHERS_NON_TLSV13; + } + } else { + // This is a different Provider then the one used by the JDK by default so we can not just assume + // the same protocols and ciphers are supported. For example even if Java11+ is used Conscrypt will + // not support TLSv1.3 and the TLSv1.3 ciphersuites. + SSLEngine engine = sslContext.createSSLEngine(); + try { + if (protocols == null) { + this.protocols = defaultProtocols(engine); + } else { + this.protocols = protocols; + } + supportedCiphers = supportedCiphers(engine); + defaultCiphers = defaultCiphers(engine, supportedCiphers); + if (!isTlsV13Supported(this.protocols)) { + // TLSv1.3 is not supported, ensure we do not include any TLSv1.3 ciphersuite. + for (String cipher: SslUtils.DEFAULT_TLSV13_CIPHER_SUITES) { + supportedCiphers.remove(cipher); + defaultCiphers.remove(cipher); + } + } + } finally { + ReferenceCountUtil.release(engine); + } + } + cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites( - ciphers, DEFAULT_CIPHERS, SUPPORTED_CIPHERS); - this.protocols = protocols == null ? DEFAULT_PROTOCOLS : protocols; + ciphers, defaultCiphers, supportedCiphers); + unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites)); - this.sslContext = checkNotNull(sslContext, "sslContext"); this.isClient = isClient; } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index e614fccdcd34..955404b25e14 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -31,6 +31,7 @@ import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; +import javax.net.ssl.SSLException; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; @@ -48,6 +49,7 @@ import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; +import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_3; /** * Tells if {@code netty-tcnative} and its OpenSSL support @@ -66,6 +68,12 @@ public final class OpenSsl { private static final boolean SUPPORTS_HOSTNAME_VALIDATION; private static final boolean USE_KEYMANAGER_FACTORY; private static final boolean SUPPORTS_OCSP; + private static final String TLSV13_CIPHERS = "TLS_AES_256_GCM_SHA384" + ':' + + "TLS_CHACHA20_POLY1305_SHA256" + ':' + + "TLS_AES_128_GCM_SHA256" + ':' + + "TLS_AES_128_CCM_8_SHA256" + ':' + + "TLS_AES_128_CCM_SHA256"; + private static final boolean TLSV13_SUPPORTED; static final Set SUPPORTED_PROTOCOLS_SET; @@ -139,17 +147,30 @@ public final class OpenSsl { boolean supportsKeyManagerFactory = false; boolean useKeyManagerFactory = false; boolean supportsHostNameValidation = false; + boolean tlsv13Supported = false; + try { final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER); long certBio = 0; SelfSignedCertificate cert = null; try { - SSLContext.setCipherSuite(sslCtx, "ALL"); + if (PlatformDependent.javaVersion() >= 11) { + try { + SSLContext.setCipherSuite(sslCtx, TLSV13_CIPHERS, true); + tlsv13Supported = true; + } catch (Exception ignore) { + tlsv13Supported = false; + } + } + SSLContext.setCipherSuite(sslCtx, "ALL", false); + final long ssl = SSL.newSSL(sslCtx, true); try { for (String c: SSL.getCiphers(ssl)) { // Filter out bad input. - if (c == null || c.isEmpty() || availableOpenSslCipherSuites.contains(c)) { + if (c == null || c.isEmpty() || availableOpenSslCipherSuites.contains(c) || + // Filter out TLSv1.3 ciphers if not supported. + !tlsv13Supported && SslUtils.isTLSv13Cipher(c)) { continue; } availableOpenSslCipherSuites.add(c); @@ -200,8 +221,13 @@ public Boolean run() { AVAILABLE_OPENSSL_CIPHER_SUITES.size() * 2); for (String cipher: AVAILABLE_OPENSSL_CIPHER_SUITES) { // Included converted but also openssl cipher name - availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "TLS")); - availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "SSL")); + if (!SslUtils.isTLSv13Cipher(cipher)) { + availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "TLS")); + availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "SSL")); + } else { + // TLSv1.3 ciphers have the correct format. + availableJavaCipherSuites.add(cipher); + } } addIfSupported(availableJavaCipherSuites, defaultCiphers, DEFAULT_CIPHER_SUITES); @@ -239,6 +265,18 @@ public Boolean run() { protocols.add(PROTOCOL_TLS_V1_2); } + // This is only supported by java11 and later. + if (tlsv13Supported && doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_3, SSL.SSL_OP_NO_TLSv1_3) + && PlatformDependent.javaVersion() >= 11) { + // We can only support TLS1.3 when using Java 11 or higher as otherwise it will fail to create the + // internal instance of an sun.security.ssl.ProtocolVersion as can not parse the version string :/ + // See http://mail.openjdk.java.net/pipermail/security-dev/2018-September/018242.html + protocols.add(PROTOCOL_TLS_V1_3); + TLSV13_SUPPORTED = true; + } else { + TLSV13_SUPPORTED = false; + } + SUPPORTED_PROTOCOLS_SET = Collections.unmodifiableSet(protocols); SUPPORTS_OCSP = doesSupportOcsp(); @@ -256,6 +294,7 @@ public Boolean run() { USE_KEYMANAGER_FACTORY = false; SUPPORTED_PROTOCOLS_SET = Collections.emptySet(); SUPPORTS_OCSP = false; + TLSV13_SUPPORTED = false; } } @@ -450,4 +489,8 @@ static void releaseIfNeeded(ReferenceCounted counted) { ReferenceCountUtil.safeRelease(counted); } } + + static boolean isTlsv13Supported() { + return TLSV13_SUPPORTED; + } } diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java index 9e524a7a0083..4972905baafa 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java @@ -24,8 +24,11 @@ import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.X509Certificate; + +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import javax.net.ssl.KeyManagerFactory; @@ -47,6 +50,12 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCountedOpenSslContext { private static final InternalLogger logger = InternalLoggerFactory.getInstance(ReferenceCountedOpenSslClientContext.class); + private static final Set SUPPORTED_KEY_TYPES = Collections.unmodifiableSet(new LinkedHashSet( + Arrays.asList(OpenSslKeyMaterialManager.KEY_TYPE_RSA, + OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA, + OpenSslKeyMaterialManager.KEY_TYPE_EC, + OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA, + OpenSslKeyMaterialManager.KEY_TYPE_EC_EC))); private final OpenSslSessionContext sessionContext; ReferenceCountedOpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, @@ -277,7 +286,8 @@ public void handle(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincip */ private static Set supportedClientKeyTypes(byte[] clientCertificateTypes) { if (clientCertificateTypes == null) { - return Collections.emptySet(); + // Try all of the supported key types. + return SUPPORTED_KEY_TYPES; } Set result = new HashSet(clientCertificateTypes.length); for (byte keyTypeCode : clientCertificateTypes) { diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index 714f473f7a7f..6f471b361a4b 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -225,26 +225,71 @@ public String run() { boolean success = false; try { try { - int opts = SSL.SSL_PROTOCOL_SSLV3 | SSL.SSL_PROTOCOL_TLSV1 | - SSL.SSL_PROTOCOL_TLSV1_1 | SSL.SSL_PROTOCOL_TLSV1_2; - ctx = SSLContext.make(opts, mode); + int protocolOpts = SSL.SSL_PROTOCOL_SSLV3 | SSL.SSL_PROTOCOL_TLSV1 | + SSL.SSL_PROTOCOL_TLSV1_1 | SSL.SSL_PROTOCOL_TLSV1_2; + if (OpenSsl.isTlsv13Supported()) { + protocolOpts |= SSL.SSL_PROTOCOL_TLSV1_3; + } + ctx = SSLContext.make(protocolOpts, mode); } catch (Exception e) { throw new SSLException("failed to create an SSL_CTX", e); } - SSLContext.setOptions(ctx, SSLContext.getOptions(ctx) | - SSL.SSL_OP_NO_SSLv2 | - SSL.SSL_OP_NO_SSLv3 | - SSL.SSL_OP_CIPHER_SERVER_PREFERENCE | + boolean tlsv13Supported = OpenSsl.isTlsv13Supported(); + StringBuilder cipherBuilder = new StringBuilder(); + StringBuilder cipherTLSv13Builder = new StringBuilder(); + + /* List the ciphers that are permitted to negotiate. */ + try { + if (unmodifiableCiphers.isEmpty()) { + // Set non TLSv1.3 ciphers. + SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, false); + if (tlsv13Supported) { + // Set TLSv1.3 ciphers. + SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, true); + } + } else { + CipherSuiteConverter.convertToCipherStrings( + unmodifiableCiphers, cipherBuilder, cipherTLSv13Builder); + + // Set non TLSv1.3 ciphers. + SSLContext.setCipherSuite(ctx, cipherBuilder.toString(), false); + if (tlsv13Supported) { + // Set TLSv1.3 ciphers. + SSLContext.setCipherSuite(ctx, cipherTLSv13Builder.toString(), true); + } + } + } catch (SSLException e) { + throw e; + } catch (Exception e) { + throw new SSLException("failed to set cipher suite: " + unmodifiableCiphers, e); + } + + int options = SSLContext.getOptions(ctx) | + SSL.SSL_OP_NO_SSLv2 | + SSL.SSL_OP_NO_SSLv3 | + // Disable TLSv1.3 by default for now. Even if TLSv1.3 is not supported this will + // work fine as in this case SSL_OP_NO_TLSv1_3 will be 0. + SSL.SSL_OP_NO_TLSv1_3 | - // We do not support compression at the moment so we should explicitly disable it. - SSL.SSL_OP_NO_COMPRESSION | + SSL.SSL_OP_CIPHER_SERVER_PREFERENCE | - // Disable ticket support by default to be more inline with SSLEngineImpl of the JDK. - // This also let SSLSession.getId() work the same way for the JDK implementation and the - // OpenSSLEngine. If tickets are supported SSLSession.getId() will only return an ID on the - // server-side if it could make use of tickets. - SSL.SSL_OP_NO_TICKET); + // We do not support compression at the moment so we should explicitly disable it. + SSL.SSL_OP_NO_COMPRESSION | + + // Disable ticket support by default to be more inline with SSLEngineImpl of the JDK. + // This also let SSLSession.getId() work the same way for the JDK implementation and the + // OpenSSLEngine. If tickets are supported SSLSession.getId() will only return an ID on the + // server-side if it could make use of tickets. + SSL.SSL_OP_NO_TICKET; + + if (cipherBuilder.length() == 0) { + // No ciphers that are compatible with SSLv2 / SSLv3 / TLSv1 / TLSv1.1 / TLSv1.2 + options |= SSL.SSL_OP_NO_SSLv2 | SSL.SSL_OP_NO_SSLv3 | SSL.SSL_OP_NO_TLSv1 + | SSL.SSL_OP_NO_TLSv1_1 | SSL.SSL_OP_NO_TLSv1_2; + } + + SSLContext.setOptions(ctx, options); // We need to enable SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER as the memory address may change between // calling OpenSSLEngine.wrap(...). @@ -255,15 +300,6 @@ public String run() { SSLContext.setTmpDHLength(ctx, DH_KEY_LENGTH); } - /* List the ciphers that are permitted to negotiate. */ - try { - SSLContext.setCipherSuite(ctx, CipherSuiteConverter.toOpenSsl(unmodifiableCiphers)); - } catch (SSLException e) { - throw e; - } catch (Exception e) { - throw new SSLException("failed to set cipher suite: " + unmodifiableCiphers, e); - } - List nextProtoList = apn.protocols(); /* Set next protocols for next protocol negotiation extension, if specified */ if (!nextProtoList.isEmpty()) { diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 49851c93b5a3..4ddcc2aefa23 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -66,9 +66,8 @@ import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; +import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_3; import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH; -import static io.netty.internal.tcnative.SSL.SSL_MAX_PLAINTEXT_LENGTH; -import static io.netty.internal.tcnative.SSL.SSL_MAX_RECORD_LENGTH; import static io.netty.util.internal.EmptyArrays.EMPTY_CERTIFICATES; import static io.netty.util.internal.EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES; import static io.netty.util.internal.ObjectUtil.checkNotNull; @@ -109,12 +108,14 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1 = 2; private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1 = 3; private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2 = 4; + private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3 = 5; private static final int[] OPENSSL_OP_NO_PROTOCOLS = { SSL.SSL_OP_NO_SSLv2, SSL.SSL_OP_NO_SSLv3, SSL.SSL_OP_NO_TLSv1, SSL.SSL_OP_NO_TLSv1_1, - SSL.SSL_OP_NO_TLSv1_2 + SSL.SSL_OP_NO_TLSv1_2, + SSL.SSL_OP_NO_TLSv1_3 }; /** * The flags argument is usually 0. @@ -124,11 +125,11 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc /** * Depends upon tcnative ... only use if tcnative is available! */ - static final int MAX_PLAINTEXT_LENGTH = SSL_MAX_PLAINTEXT_LENGTH; + static final int MAX_PLAINTEXT_LENGTH = SSL.SSL_MAX_PLAINTEXT_LENGTH; /** * Depends upon tcnative ... only use if tcnative is available! */ - private static final int MAX_RECORD_SIZE = SSL_MAX_RECORD_LENGTH; + private static final int MAX_RECORD_SIZE = SSL.SSL_MAX_RECORD_LENGTH; private static final AtomicIntegerFieldUpdater DESTROYED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(ReferenceCountedOpenSslEngine.class, "destroyed"); @@ -1206,7 +1207,10 @@ private void rejectRemoteInitiatedRenegotiation() throws SSLHandshakeException { // As rejectRemoteInitiatedRenegotiation() is called in a finally block we also need to check if we shutdown // the engine before as otherwise SSL.getHandshakeCount(ssl) will throw an NPE if the passed in ssl is 0. // See https://github.com/netty/netty/issues/7353 - if (!isDestroyed() && SSL.getHandshakeCount(ssl) > 1) { + if (!isDestroyed() && SSL.getHandshakeCount(ssl) > 1 && + // As we may count multiple handshakes when TLSv1.3 is used we should just ignore this here as + // renegotiation is not supported in TLSv1.3 as per spec. + !SslUtils.PROTOCOL_TLS_V1_3.equals(session.getProtocol()) && handshakeState == HandshakeState.FINISHED) { // TODO: In future versions me may also want to send a fatal_alert to the client and so notify it // that the renegotiation failed. shutdown(); @@ -1379,15 +1383,18 @@ public final String[] getEnabledCipherSuites() { if (enabled == null) { return EmptyArrays.EMPTY_STRINGS; } else { + List enabledList = new ArrayList(); synchronized (this) { for (int i = 0; i < enabled.length; i++) { String mapped = toJavaCipherSuite(enabled[i]); - if (mapped != null) { - enabled[i] = mapped; + final String cipher = mapped == null ? enabled[i] : mapped; + if (!OpenSsl.isTlsv13Supported() && SslUtils.isTLSv13Cipher(cipher)) { + continue; } + enabledList.add(cipher); } } - return enabled; + return enabledList.toArray(new String[0]); } } @@ -1396,35 +1403,28 @@ public final void setEnabledCipherSuites(String[] cipherSuites) { checkNotNull(cipherSuites, "cipherSuites"); final StringBuilder buf = new StringBuilder(); - for (String c: cipherSuites) { - if (c == null) { - break; - } - - String converted = CipherSuiteConverter.toOpenSsl(c); - if (converted == null) { - converted = c; - } - - if (!OpenSsl.isCipherSuiteAvailable(converted)) { - throw new IllegalArgumentException("unsupported cipher suite: " + c + '(' + converted + ')'); - } - - buf.append(converted); - buf.append(':'); - } - - if (buf.length() == 0) { - throw new IllegalArgumentException("empty cipher suites"); - } - buf.setLength(buf.length() - 1); + final StringBuilder bufTLSv13 = new StringBuilder(); + CipherSuiteConverter.convertToCipherStrings(Arrays.asList(cipherSuites), buf, bufTLSv13); final String cipherSuiteSpec = buf.toString(); + final String cipherSuiteSpecTLSv13 = bufTLSv13.toString(); + if (!OpenSsl.isTlsv13Supported() && !cipherSuiteSpecTLSv13.isEmpty()) { + throw new IllegalArgumentException("TLSv1.3 is not supported by this java version."); + } synchronized (this) { if (!isDestroyed()) { + // TODO: Should we also adjust the protocols based on if there are any ciphers left that can be used + // for TLSv1.3 or for previor SSL/TLS versions ? try { - SSL.setCipherSuites(ssl, cipherSuiteSpec); + // Set non TLSv1.3 ciphers. + SSL.setCipherSuites(ssl, cipherSuiteSpec, false); + + if (OpenSsl.isTlsv13Supported()) { + // Set TLSv1.3 ciphers. + SSL.setCipherSuites(ssl, cipherSuiteSpecTLSv13, true); + } + } catch (Exception e) { throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec, e); } @@ -1462,6 +1462,9 @@ public final String[] getEnabledProtocols() { if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_2, PROTOCOL_TLS_V1_2)) { enabled.add(PROTOCOL_TLS_V1_2); } + if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_3, PROTOCOL_TLS_V1_3)) { + enabled.add(PROTOCOL_TLS_V1_3); + } if (isProtocolEnabled(opts, SSL.SSL_OP_NO_SSLv2, PROTOCOL_SSL_V2)) { enabled.add(PROTOCOL_SSL_V2); } @@ -1533,13 +1536,20 @@ public final void setEnabledProtocols(String[] protocols) { if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2) { maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2; } + } else if (p.equals(PROTOCOL_TLS_V1_3)) { + if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3) { + minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3; + } + if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3) { + maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3; + } } } synchronized (this) { if (!isDestroyed()) { // Clear out options which disable protocols SSL.clearOptions(ssl, SSL.SSL_OP_NO_SSLv2 | SSL.SSL_OP_NO_SSLv3 | SSL.SSL_OP_NO_TLSv1 | - SSL.SSL_OP_NO_TLSv1_1 | SSL.SSL_OP_NO_TLSv1_2); + SSL.SSL_OP_NO_TLSv1_1 | SSL.SSL_OP_NO_TLSv1_2 | SSL.SSL_OP_NO_TLSv1_3); int opts = 0; for (int i = 0; i < minProtocolIndex; ++i) { diff --git a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java b/handler/src/main/java/io/netty/handler/ssl/SslUtils.java index 414c9d1d18f8..f1f000fa8fd4 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslUtils.java @@ -22,9 +22,14 @@ import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.base64.Base64Dialect; import io.netty.util.NetUtil; +import io.netty.util.internal.EmptyArrays; +import io.netty.util.internal.PlatformDependent; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -36,7 +41,11 @@ * Constants for SSL packets. */ final class SslUtils { - + // See https://tools.ietf.org/html/rfc8446#appendix-B.4 + private static final Set TLSV13_CIPHERS = Collections.unmodifiableSet(new HashSet( + asList("TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256", + "TLS_AES_128_GCM_SHA256", "TLS_AES_128_CCM_8_SHA256", + "TLS_AES_128_CCM_SHA256"))); // Protocols static final String PROTOCOL_SSL_V2_HELLO = "SSLv2Hello"; static final String PROTOCOL_SSL_V2 = "SSLv2"; @@ -44,6 +53,7 @@ final class SslUtils { static final String PROTOCOL_TLS_V1 = "TLSv1"; static final String PROTOCOL_TLS_V1_1 = "TLSv1.1"; static final String PROTOCOL_TLS_V1_2 = "TLSv1.2"; + static final String PROTOCOL_TLS_V1_3 = "TLSv1.3"; /** * change cipher spec @@ -85,20 +95,36 @@ final class SslUtils { */ static final int NOT_ENCRYPTED = -2; - static final String[] DEFAULT_CIPHER_SUITES = { + static final String[] DEFAULT_CIPHER_SUITES; + static final String[] DEFAULT_TLSV13_CIPHER_SUITES; + + static { + if (PlatformDependent.javaVersion() >= 11) { + DEFAULT_TLSV13_CIPHER_SUITES = new String[] { "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" }; + } else { + DEFAULT_TLSV13_CIPHER_SUITES = EmptyArrays.EMPTY_STRINGS; + } + + List defaultCiphers = new ArrayList(); // GCM (Galois/Counter Mode) requires JDK 8. - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + defaultCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"); + defaultCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"); + defaultCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); + defaultCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); // AES256 requires JCE unlimited strength jurisdiction policy files. - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + defaultCiphers.add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"); // GCM (Galois/Counter Mode) requires JDK 8. - "TLS_RSA_WITH_AES_128_GCM_SHA256", - "TLS_RSA_WITH_AES_128_CBC_SHA", + defaultCiphers.add("TLS_RSA_WITH_AES_128_GCM_SHA256"); + defaultCiphers.add("TLS_RSA_WITH_AES_128_CBC_SHA"); // AES256 requires JCE unlimited strength jurisdiction policy files. - "TLS_RSA_WITH_AES_256_CBC_SHA" - }; + defaultCiphers.add("TLS_RSA_WITH_AES_256_CBC_SHA"); + + for (String tlsv13Cipher: DEFAULT_TLSV13_CIPHER_SUITES) { + defaultCiphers.add(tlsv13Cipher); + } + + DEFAULT_CIPHER_SUITES = defaultCiphers.toArray(new String[0]); + } /** * Add elements from {@code names} into {@code enabled} if they are in {@code supported}. @@ -361,6 +387,14 @@ static boolean isValidHostNameForSNI(String hostname) { !NetUtil.isValidIpV6Address(hostname); } + /** + * Returns {@code true} if the the given cipher (in openssl format) is for TLSv1.3, {@code false} otherwise. + */ + static boolean isTLSv13Cipher(String cipher) { + // See https://tools.ietf.org/html/rfc8446#appendix-B.4 + return TLSV13_CIPHERS.contains(cipher); + } + private SslUtils() { } } diff --git a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java index 22e5f43bf880..9c394ccf6371 100644 --- a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java @@ -125,12 +125,16 @@ public void testHandshake() throws Exception { final SslContext sslServerContext = SslContextBuilder.forServer(CERT.certificate(), CERT.privateKey()) .sslProvider(serverSslProvider) .ciphers(ciphers) + // As this is not a TLSv1.3 cipher we should ensure we talk something else. + .protocols(SslUtils.PROTOCOL_TLS_V1_2) .build(); try { final SslContext sslClientContext = SslContextBuilder.forClient() .sslProvider(clientSslProvider) .ciphers(ciphers) + // As this is not a TLSv1.3 cipher we should ensure we talk something else. + .protocols(SslUtils.PROTOCOL_TLS_V1_2) .trustManager(InsecureTrustManagerFactory.INSTANCE) .build(); diff --git a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteConverterTest.java b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteConverterTest.java index ffe53d2ba8b2..f70da234c707 100644 --- a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteConverterTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteConverterTest.java @@ -22,8 +22,7 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; public class CipherSuiteConverterTest { diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java index 870f71ef364a..d666535b194b 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java @@ -31,17 +31,17 @@ @RunWith(Parameterized.class) public class ConscryptJdkSslEngineInteropTest extends SSLEngineTest { - @Parameterized.Parameters(name = "{index}: bufferType = {0}") - public static Collection data() { - List params = new ArrayList(); + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + public static Collection data() { + List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(type); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); } return params; } - public ConscryptJdkSslEngineInteropTest(BufferType type) { - super(type); + public ConscryptJdkSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo) { + super(type, combo); } @BeforeClass diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java index e57fd58be0fd..8c6121b6fb59 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java @@ -30,17 +30,17 @@ @RunWith(Parameterized.class) public class ConscryptSslEngineTest extends SSLEngineTest { - @Parameterized.Parameters(name = "{index}: bufferType = {0}") - public static Collection data() { - List params = new ArrayList(); + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + public static Collection data() { + List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(type); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); } return params; } - public ConscryptSslEngineTest(BufferType type) { - super(type); + public ConscryptSslEngineTest(BufferType type, ProtocolCipherCombo combo) { + super(type, combo); } @BeforeClass diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java index 6d8862a0f23d..309490af592a 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java @@ -16,6 +16,7 @@ package io.netty.handler.ssl; import java.security.Provider; + import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -31,17 +32,17 @@ @RunWith(Parameterized.class) public class JdkConscryptSslEngineInteropTest extends SSLEngineTest { - @Parameterized.Parameters(name = "{index}: bufferType = {0}") - public static Collection data() { - List params = new ArrayList(); + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + public static Collection data() { + List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(type); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); } return params; } - public JdkConscryptSslEngineInteropTest(BufferType type) { - super(type); + public JdkConscryptSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo) { + super(type, combo); } @BeforeClass diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java index 0eed0b3087d5..a85b665ff2cc 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java @@ -15,6 +15,7 @@ */ package io.netty.handler.ssl; +import io.netty.util.internal.PlatformDependent; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -32,17 +33,21 @@ @RunWith(Parameterized.class) public class JdkOpenSslEngineInteroptTest extends SSLEngineTest { - @Parameterized.Parameters(name = "{index}: bufferType = {0}") - public static Collection data() { - List params = new ArrayList(); + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + public static Collection data() { + List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(type); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); + + if (PlatformDependent.javaVersion() >= 11 && OpenSsl.isTlsv13Supported()) { + params.add(new Object[] { type, ProtocolCipherCombo.tlsv13() }); + } } return params; } - public JdkOpenSslEngineInteroptTest(BufferType type) { - super(type); + public JdkOpenSslEngineInteroptTest(BufferType type, ProtocolCipherCombo protocolCipherCombo) { + super(type, protocolCipherCombo); } @BeforeClass diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java index f37a6aff2512..74f000fd01e0 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Collection; +import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.PlatformDependent; import org.junit.Ignore; import org.junit.Test; @@ -141,12 +142,15 @@ final void activate(JdkSslEngineTest instance) { private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1"; private static final String APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE = "my-protocol-FOO"; - @Parameterized.Parameters(name = "{index}: providerType = {0}, bufferType = {1}") + @Parameterized.Parameters(name = "{index}: providerType = {0}, bufferType = {1}, combo = {2}") public static Collection data() { List params = new ArrayList(); for (ProviderType providerType : ProviderType.values()) { for (BufferType bufferType : BufferType.values()) { - params.add(new Object[]{providerType, bufferType}); + params.add(new Object[]{ providerType, bufferType, ProtocolCipherCombo.tlsv12()}); + if (PlatformDependent.javaVersion() >= 11) { + params.add(new Object[] { providerType, bufferType, ProtocolCipherCombo.tlsv13() }); + } } } return params; @@ -156,8 +160,8 @@ public static Collection data() { private Provider provider; - public JdkSslEngineTest(ProviderType providerType, BufferType bufferType) { - super(bufferType); + public JdkSslEngineTest(ProviderType providerType, BufferType bufferType, ProtocolCipherCombo protocolCipherCombo) { + super(bufferType, protocolCipherCombo); this.providerType = providerType; } @@ -235,9 +239,11 @@ public String select(List protocols) { InsecureTrustManagerFactory.INSTANCE, null, IdentityCipherSuiteFilter.INSTANCE, clientApn, 0, 0); - setupHandlers(serverSslCtx, clientSslCtx); + setupHandlers(new TestDelegatingSslContext(serverSslCtx), new TestDelegatingSslContext(clientSslCtx)); assertTrue(clientLatch.await(2, TimeUnit.SECONDS)); - assertTrue(clientException instanceof SSLHandshakeException); + // When using TLSv1.3 the handshake is NOT sent in an extra round trip which means there will be + // no exception reported in this case but just the channel will be closed. + assertTrue(clientException instanceof SSLHandshakeException || clientException == null); } } catch (SkipTestException e) { // ALPN availability is dependent on the java version. If ALPN is not available because of @@ -358,4 +364,16 @@ private static final class SkipTestException extends RuntimeException { super(message); } } + + private final class TestDelegatingSslContext extends DelegatingSslContext { + TestDelegatingSslContext(SslContext ctx) { + super(ctx); + } + + @Override + protected void initEngine(SSLEngine engine) { + engine.setEnabledProtocols(protocols()); + engine.setEnabledCipherSuites(ciphers().toArray(EmptyArrays.EMPTY_STRINGS)); + } + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index 99fb8fa82e23..630de226d6d9 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -67,17 +67,21 @@ public class OpenSslEngineTest extends SSLEngineTest { private static final String PREFERRED_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http2"; private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1"; - @Parameterized.Parameters(name = "{index}: bufferType = {0}") - public static Collection data() { - List params = new ArrayList(); + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + public static Collection data() { + List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(type); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); + + if (PlatformDependent.javaVersion() >= 11 && OpenSsl.isTlsv13Supported()) { + params.add(new Object[] { type, ProtocolCipherCombo.tlsv13() }); + } } return params; } - public OpenSslEngineTest(BufferType type) { - super(type); + public OpenSslEngineTest(BufferType type, ProtocolCipherCombo cipherCombo) { + super(type, cipherCombo); } @BeforeClass @@ -206,13 +210,17 @@ public void testEnablingAnAlreadyDisabledSslProtocol() throws Exception { @Test public void testWrapBuffersNoWritePendingError() throws Exception { clientSslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build(); + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build(); + .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { @@ -240,13 +248,17 @@ public void testWrapBuffersNoWritePendingError() throws Exception { @Test public void testOnlySmallBufferNeededForWrap() throws Exception { clientSslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build(); + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build(); + .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { @@ -291,13 +303,17 @@ public void testOnlySmallBufferNeededForWrap() throws Exception { @Test public void testNeededDstCapacityIsCorrectlyCalculated() throws Exception { clientSslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build(); + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build(); + .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { @@ -327,13 +343,17 @@ public void testNeededDstCapacityIsCorrectlyCalculated() throws Exception { @Test public void testSrcsLenOverFlowCorrectlyHandled() throws Exception { clientSslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build(); + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build(); + .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { @@ -374,9 +394,11 @@ public void testSrcsLenOverFlowCorrectlyHandled() throws Exception { @Test public void testCalculateOutNetBufSizeOverflow() throws SSLException { clientSslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build(); + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SSLEngine clientEngine = null; try { clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); @@ -390,9 +412,11 @@ public void testCalculateOutNetBufSizeOverflow() throws SSLException { @Test public void testCalculateOutNetBufSize0() throws SSLException { clientSslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build(); + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SSLEngine clientEngine = null; try { clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); @@ -415,13 +439,17 @@ public void testCorrectlyCalculateSpaceForAlertJDKCompatabilityModeOff() throws private void testCorrectlyCalculateSpaceForAlert(boolean jdkCompatabilityMode) throws Exception { SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build(); + .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); clientSslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build(); + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SSLEngine clientEngine = null; SSLEngine serverEngine = null; try { @@ -473,13 +501,13 @@ protected void mySetupMutualAuthServerInitSslHandler(SslHandler handler) { @Test public void testWrapWithDifferentSizesTLSv1() throws Exception { clientSslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build(); + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .build(); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build(); + .sslProvider(sslServerProvider()) + .build(); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "AES128-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1, "ECDHE-RSA-AES128-SHA"); @@ -504,13 +532,13 @@ public void testWrapWithDifferentSizesTLSv1() throws Exception { @Test public void testWrapWithDifferentSizesTLSv1_1() throws Exception { clientSslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build(); + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .build(); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build(); + .sslProvider(sslServerProvider()) + .build(); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "ECDHE-RSA-AES256-SHA"); testWrapWithDifferentSizes(PROTOCOL_TLS_V1_1, "AES256-SHA"); @@ -613,12 +641,16 @@ public void testMultipleRecordsInOneBufferWithNonZeroPositionJDKCompatabilityMod .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); @@ -690,12 +722,16 @@ public void testInputTooBigAndFillsUpBuffersJDKCompatabilityModeOff() throws Exc .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); @@ -774,12 +810,16 @@ public void testPartialPacketUnwrapJDKCompatabilityModeOff() throws Exception { .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); @@ -849,12 +889,16 @@ public void testBufferUnderFlowAvoidedIfJDKCompatabilityModeOff() throws Excepti .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); @@ -982,8 +1026,10 @@ public void testSNIMatchersDoesNotThrow() throws Exception { assumeTrue(PlatformDependent.javaVersion() >= 8); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build(); + .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { @@ -1002,8 +1048,10 @@ public void testSNIMatchersWithSNINameWithUnderscore() throws Exception { byte[] name = "rb8hx3pww30y3tvw0mwy.v1_1".getBytes(CharsetUtil.UTF_8); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build(); + .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { @@ -1022,8 +1070,10 @@ public void testSNIMatchersWithSNINameWithUnderscore() throws Exception { public void testAlgorithmConstraintsThrows() throws Exception { SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build(); + .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); try { diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java index d2a00c5432ce..bc1106e7a345 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java @@ -15,6 +15,7 @@ */ package io.netty.handler.ssl; +import io.netty.util.internal.PlatformDependent; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -34,17 +35,21 @@ @RunWith(Parameterized.class) public class OpenSslJdkSslEngineInteroptTest extends SSLEngineTest { - @Parameterized.Parameters(name = "{index}: bufferType = {0}") - public static Collection data() { - List params = new ArrayList(); + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + public static Collection data() { + List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(type); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); + + if (PlatformDependent.javaVersion() >= 11 && OpenSsl.isTlsv13Supported()) { + params.add(new Object[] { type, ProtocolCipherCombo.tlsv13() }); + } } return params; } - public OpenSslJdkSslEngineInteroptTest(BufferType type) { - super(type); + public OpenSslJdkSslEngineInteroptTest(BufferType type, ProtocolCipherCombo combo) { + super(type, combo); } @BeforeClass diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java index e8c46ed7a056..0a3ff97f3245 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java @@ -15,6 +15,12 @@ */ package io.netty.handler.ssl; +import io.netty.util.internal.PlatformDependent; + +import java.util.Arrays; +import java.util.Collections; + +import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; import static org.junit.Assume.assumeTrue; final class OpenSslTestUtils { @@ -28,4 +34,17 @@ static void checkShouldUseKeyManagerFactory() { static boolean isBoringSSL() { return "BoringSSL".equals(OpenSsl.versionString()); } + + static SslContextBuilder configureProtocolForMutualAuth( + SslContextBuilder ctx, SslProvider sslClientProvider, SslProvider sslServerProvider) { + if (PlatformDependent.javaVersion() >= 11 + && sslClientProvider == SslProvider.JDK && sslServerProvider != SslProvider.JDK) { + // Make sure we do not use TLSv1.3 as there seems to be a bug currently in the JDK TLSv1.3 implementation. + // See: + // - http://mail.openjdk.java.net/pipermail/security-dev/2018-September/018191.html + // - https://bugs.openjdk.java.net/projects/JDK/issues/JDK-8210846 + ctx.protocols(PROTOCOL_TLS_V1_2).ciphers(Collections.singleton("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")); + } + return ctx; + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java index 2abc33e8a310..780ddf670eec 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java @@ -381,12 +381,21 @@ private void testCloseNotify(final long closeNotifyReadTimeout, final boolean ti SelfSignedCertificate ssc = new SelfSignedCertificate(); final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(serverProvider) - .build(); + .sslProvider(serverProvider) + // Use TLSv1.2 as we depend on the fact that the handshake + // is done in an extra round trip in the test which + // is not true in TLSv1.3 + .protocols(SslUtils.PROTOCOL_TLS_V1_2) + .build(); final SslContext sslClientCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(clientProvider).build(); + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(clientProvider) + // Use TLSv1.2 as we depend on the fact that the handshake + // is done in an extra round trip in the test which + // is not true in TLSv1.3 + .protocols(SslUtils.PROTOCOL_TLS_V1_2) + .build(); EventLoopGroup group = new NioEventLoopGroup(); Channel sc = null; diff --git a/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java index ddbd0b16dedb..588619d3a7cb 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java @@ -23,8 +23,8 @@ public class ReferenceCountedOpenSslEngineTest extends OpenSslEngineTest { - public ReferenceCountedOpenSslEngineTest(BufferType type) { - super(type); + public ReferenceCountedOpenSslEngineTest(BufferType type, ProtocolCipherCombo combo) { + super(type, combo); } @Override @@ -60,9 +60,11 @@ protected void cleanupServerSslEngine(SSLEngine engine) { @Test(expected = NullPointerException.class) public void testNotLeakOnException() throws Exception { clientSslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build(); + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); clientSslCtx.newEngine(null); } diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 4248e1fca48d..0094c92d3ec1 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -33,6 +33,7 @@ import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SimpleTrustManagerFactory; @@ -67,6 +68,7 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -79,6 +81,7 @@ import javax.net.ssl.SNIHostName; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.Status; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLParameters; @@ -89,14 +92,7 @@ import javax.net.ssl.X509TrustManager; import javax.security.cert.X509Certificate; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2_HELLO; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V3; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; -import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH; - +import static io.netty.handler.ssl.SslUtils.*; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -220,10 +216,42 @@ enum BufferType { Mixed } + static final class ProtocolCipherCombo { + private static final ProtocolCipherCombo TLSV12 = new ProtocolCipherCombo( + PROTOCOL_TLS_V1_2, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); + private static final ProtocolCipherCombo TLSV13 = new ProtocolCipherCombo( + PROTOCOL_TLS_V1_3, "TLS_AES_128_GCM_SHA256"); + final String protocol; + final String cipher; + + private ProtocolCipherCombo(String protocol, String cipher) { + this.protocol = protocol; + this.cipher = cipher; + } + + static ProtocolCipherCombo tlsv12() { + return TLSV12; + } + + static ProtocolCipherCombo tlsv13() { + return TLSV13; + } + + @Override + public String toString() { + return "ProtocolCipherCombo{" + + "protocol='" + protocol + '\'' + + ", cipher='" + cipher + '\'' + + '}'; + } + } + private final BufferType type; + private final ProtocolCipherCombo protocolCipherCombo; - protected SSLEngineTest(BufferType type) { + protected SSLEngineTest(BufferType type, ProtocolCipherCombo protocolCipherCombo) { this.type = type; + this.protocolCipherCombo = protocolCipherCombo; } protected ByteBuffer allocateBuffer(int len) { @@ -620,36 +648,46 @@ protected boolean mySetupMutualAuthServerIsValidClientException(Throwable cause) } protected boolean mySetupMutualAuthServerIsValidException(Throwable cause) { - return cause instanceof SSLHandshakeException || cause instanceof ClosedChannelException; + // As in TLSv1.3 the handshake is sent without an extra roundtrip an SSLException is valid as well. + return cause instanceof SSLException || cause instanceof ClosedChannelException; } protected void mySetupMutualAuthServerInitSslHandler(SslHandler handler) { } + private SslContextBuilder configureProtocolForMutualAuth(SslContextBuilder ctx) { + return OpenSslTestUtils.configureProtocolForMutualAuth(ctx, sslClientProvider(), sslServerProvider()); + } + private void mySetupMutualAuth(KeyManagerFactory serverKMF, final File serverTrustManager, KeyManagerFactory clientKMF, File clientTrustManager, ClientAuth clientAuth, final boolean failureExpected, final boolean serverInitEngine) throws SSLException, InterruptedException { - serverSslCtx = SslContextBuilder.forServer(serverKMF) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .trustManager(serverTrustManager) - .clientAuth(clientAuth) - .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) - .sessionCacheSize(0) - .sessionTimeout(0) - .build(); + serverSslCtx = configureProtocolForMutualAuth( + SslContextBuilder.forServer(serverKMF) + .protocols(protocols()) + .ciphers(ciphers()) + .sslProvider(sslServerProvider()) + .sslContextProvider(serverSslContextProvider()) + .trustManager(serverTrustManager) + .clientAuth(clientAuth) + .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) + .sessionCacheSize(0) + .sessionTimeout(0)).build(); + + clientSslCtx = configureProtocolForMutualAuth( + SslContextBuilder.forClient() + .protocols(protocols()) + .ciphers(ciphers()) + .sslProvider(sslClientProvider()) + .sslContextProvider(clientSslContextProvider()) + .trustManager(clientTrustManager) + .keyManager(clientKMF) + .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) + .sessionCacheSize(0) + .sessionTimeout(0)).build(); - clientSslCtx = SslContextBuilder.forClient() - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .trustManager(clientTrustManager) - .keyManager(clientKMF) - .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) - .sessionCacheSize(0) - .sessionTimeout(0) - .build(); serverConnectedChannel = null; sb = new ServerBootstrap(); cb = new Bootstrap(); @@ -711,10 +749,11 @@ protected void initChannel(Channel ch) throws Exception { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt == SslHandshakeCompletionEvent.SUCCESS) { - if (failureExpected) { - clientException = new IllegalStateException("handshake complete. expected failure"); + // With TLS1.3 a mutal auth error will not be propagated as a handshake error most of the + // time as the handshake needs NO extra roundtrip. + if (!failureExpected) { + clientLatch.countDown(); } - clientLatch.countDown(); } else if (evt instanceof SslHandshakeCompletionEvent) { clientException = ((SslHandshakeCompletionEvent) evt).cause(); clientLatch.countDown(); @@ -724,7 +763,7 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (cause.getCause() instanceof SSLHandshakeException) { + if (cause.getCause() instanceof SSLException) { clientException = cause.getCause(); clientLatch.countDown(); } else { @@ -735,7 +774,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E } }); - serverChannel = sb.bind(new InetSocketAddress(0)).sync().channel(); + serverChannel = sb.bind(new InetSocketAddress(8443)).sync().channel(); int port = ((InetSocketAddress) serverChannel.localAddress()).getPort(); ChannelFuture ccf = cb.connect(new InetSocketAddress(NetUtil.LOCALHOST, port)); @@ -776,6 +815,8 @@ private void mySetupClientHostnameValidation(File serverCrtFile, File serverKeyF final String expectedHost = "localhost"; serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile, null) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .sslContextProvider(serverSslContextProvider()) .trustManager(InsecureTrustManagerFactory.INSTANCE) .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) @@ -785,12 +826,15 @@ private void mySetupClientHostnameValidation(File serverCrtFile, File serverKeyF clientSslCtx = SslContextBuilder.forClient() .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .sslContextProvider(clientSslContextProvider()) .trustManager(clientTrustCrtFile) .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) .sessionCacheSize(0) .sessionTimeout(0) .build(); + serverConnectedChannel = null; sb = new ServerBootstrap(); cb = new Bootstrap(); @@ -897,24 +941,28 @@ private void mySetupMutualAuth( File servertTrustCrtFile, File serverKeyFile, final File serverCrtFile, String serverKeyPassword, File clientTrustCrtFile, File clientKeyFile, File clientCrtFile, String clientKeyPassword) throws InterruptedException, SSLException { - serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile, serverKeyPassword) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .trustManager(servertTrustCrtFile) - .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) - .sessionCacheSize(0) - .sessionTimeout(0) - .build(); + serverSslCtx = configureProtocolForMutualAuth( + SslContextBuilder.forServer(serverCrtFile, serverKeyFile, serverKeyPassword) + .sslProvider(sslServerProvider()) + .sslContextProvider(serverSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .trustManager(servertTrustCrtFile) + .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) + .sessionCacheSize(0) + .sessionTimeout(0)).build(); + clientSslCtx = configureProtocolForMutualAuth( + SslContextBuilder.forClient() + .sslProvider(sslClientProvider()) + .sslContextProvider(clientSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .trustManager(clientTrustCrtFile) + .keyManager(clientCrtFile, clientKeyFile, clientKeyPassword) + .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) + .sessionCacheSize(0) + .sessionTimeout(0)).build(); - clientSslCtx = SslContextBuilder.forClient() - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .trustManager(clientTrustCrtFile) - .keyManager(clientCrtFile, clientKeyFile, clientKeyPassword) - .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) - .sessionCacheSize(0) - .sessionTimeout(0) - .build(); serverConnectedChannel = null; sb = new ServerBootstrap(); cb = new Bootstrap(); @@ -930,6 +978,7 @@ protected void initChannel(Channel ch) { SSLEngine engine = serverSslCtx.newEngine(ch.alloc()); engine.setUseClientMode(false); engine.setNeedClientAuth(true); + p.addLast(new SslHandler(engine)); p.addLast(new MessageDelegatorChannelHandler(serverReceiver, serverLatch)); p.addLast(new ChannelInboundHandlerAdapter() { @@ -986,13 +1035,14 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc protected void initChannel(Channel ch) throws Exception { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); + SslHandler handler = clientSslCtx.newHandler(ch.alloc()); + handler.engine().setNeedClientAuth(true); ChannelPipeline p = ch.pipeline(); - p.addLast(clientSslCtx.newHandler(ch.alloc())); + p.addLast(handler); p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch)); p.addLast(new ChannelInboundHandlerAdapter() { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); if (cause.getCause() instanceof SSLHandshakeException) { clientException = cause.getCause(); clientLatch.countDown(); @@ -1081,11 +1131,15 @@ public void testSessionInvalidate() throws Exception { .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) .sslContextProvider(clientSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) .sslContextProvider(serverSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine clientEngine = null; SSLEngine serverEngine = null; @@ -1110,11 +1164,15 @@ public void testSSLSessionId() throws Exception { clientSslCtx = SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .sslProvider(sslClientProvider()) + // This test only works for non TLSv1.3 for now + .protocols(PROTOCOL_TLS_V1_2) .sslContextProvider(clientSslContextProvider()) .build(); SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) + // This test only works for non TLSv1.3 for now + .protocols(PROTOCOL_TLS_V1_2) .sslContextProvider(serverSslContextProvider()) .build(); SSLEngine clientEngine = null; @@ -1145,8 +1203,11 @@ public void clientInitiatedRenegotiationWithFatalAlertDoesNotInfiniteLoopServer( throws CertificateException, SSLException, InterruptedException, ExecutionException { final SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()).build(); + .sslProvider(sslServerProvider()) + .sslContextProvider(serverSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); sb = new ServerBootstrap() .group(new NioEventLoopGroup(1)) .channel(NioServerSocketChannel.class) @@ -1196,8 +1257,12 @@ public void channelInactive(ChannelHandlerContext ctx) { serverChannel = sb.bind(new InetSocketAddress(0)).syncUninterruptibly().channel(); clientSslCtx = SslContextBuilder.forClient() - .sslProvider(SslProvider.JDK) // OpenSslEngine doesn't support renegotiation on client side - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); + // OpenSslEngine doesn't support renegotiation on client side + .sslProvider(SslProvider.JDK) + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); cb = new Bootstrap(); cb.group(new NioEventLoopGroup(1)) @@ -1257,9 +1322,11 @@ protected void testEnablingAnAlreadyDisabledSslProtocol(String[] protocols1, Str File serverKeyFile = new File(getClass().getResource("test_unencrypted.pem").getFile()); File serverCrtFile = new File(getClass().getResource("test.crt").getFile()); serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .build(); + .sslProvider(sslServerProvider()) + .sslContextProvider(serverSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); sslEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -1338,7 +1405,10 @@ protected void handshake(SSLEngine clientEngine, SSLEngine serverEngine) throws cTOsPos = cTOs.position(); sTOcPos = sTOc.position(); - if (!clientHandshakeFinished) { + if (!clientHandshakeFinished || + // After the handshake completes it is possible we have more data that was send by the server as + // the server will send session updates after the handshake. In this case continue to unwrap. + SslUtils.PROTOCOL_TLS_V1_3.equals(clientEngine.getSession().getProtocol())) { int clientAppReadBufferPos = clientAppReadBuffer.position(); clientResult = clientEngine.unwrap(sTOc, clientAppReadBuffer); @@ -1350,7 +1420,7 @@ protected void handshake(SSLEngine clientEngine, SSLEngine serverEngine) throws clientHandshakeFinished = true; } } else { - assertFalse(sTOc.hasRemaining()); + assertEquals(0, sTOc.remaining()); } if (!serverHandshakeFinished) { @@ -1433,24 +1503,35 @@ protected void setupHandlers(ApplicationProtocolConfig serverApn, ApplicationPro SelfSignedCertificate ssc = new SelfSignedCertificate(); try { - setupHandlers(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey(), null) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) - .applicationProtocolConfig(serverApn) - .sessionCacheSize(0) - .sessionTimeout(0) - .build(), - - SslContextBuilder.forClient() - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .applicationProtocolConfig(clientApn) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) - .sessionCacheSize(0) - .sessionTimeout(0) - .build()); + SslContextBuilder serverCtxBuilder = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey(), null) + .sslProvider(sslServerProvider()) + .sslContextProvider(serverSslContextProvider()) + .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) + .applicationProtocolConfig(serverApn) + .sessionCacheSize(0) + .sessionTimeout(0); + if (serverApn.protocol() == Protocol.NPN || serverApn.protocol() == Protocol.NPN_AND_ALPN) { + // NPN is not really well supported with TLSv1.3 so force to use TLSv1.2 + // See https://github.com/openssl/openssl/issues/3665 + serverCtxBuilder.protocols(PROTOCOL_TLS_V1_2); + } + + SslContextBuilder clientCtxBuilder = SslContextBuilder.forClient() + .sslProvider(sslClientProvider()) + .sslContextProvider(clientSslContextProvider()) + .applicationProtocolConfig(clientApn) + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) + .sessionCacheSize(0) + .sessionTimeout(0); + + if (clientApn.protocol() == Protocol.NPN || clientApn.protocol() == Protocol.NPN_AND_ALPN) { + // NPN is not really well supported with TLSv1.3 so force to use TLSv1.2 + // See https://github.com/openssl/openssl/issues/3665 + clientCtxBuilder.protocols(PROTOCOL_TLS_V1_2); + } + + setupHandlers(serverCtxBuilder.build(), clientCtxBuilder.build()); } finally { ssc.delete(); } @@ -1511,6 +1592,11 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E ctx.fireExceptionCaught(cause); } } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + clientLatch.countDown(); + } }); } }); @@ -1524,12 +1610,15 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E @Test(timeout = 30000) public void testMutualAuthSameCertChain() throws Exception { - serverSslCtx = SslContextBuilder.forServer( - new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8)), - new ByteArrayInputStream(PRIVATE_KEY_PEM.getBytes(CharsetUtil.UTF_8))) - .trustManager(new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8))) - .clientAuth(ClientAuth.REQUIRE).sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()).build(); + serverSslCtx = configureProtocolForMutualAuth( + SslContextBuilder.forServer( + new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8)), + new ByteArrayInputStream(PRIVATE_KEY_PEM.getBytes(CharsetUtil.UTF_8))) + .trustManager(new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8))) + .clientAuth(ClientAuth.REQUIRE).sslProvider(sslServerProvider()) + .sslContextProvider(serverSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers())).build(); sb = new ServerBootstrap(); sb.group(new NioEventLoopGroup(), new NioEventLoopGroup()); @@ -1580,13 +1669,14 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc } }).bind(new InetSocketAddress(0)).syncUninterruptibly().channel(); - clientSslCtx = SslContextBuilder.forClient() - .keyManager( + clientSslCtx = configureProtocolForMutualAuth( + SslContextBuilder.forClient().keyManager( new ByteArrayInputStream(CLIENT_X509_CERT_CHAIN_PEM.getBytes(CharsetUtil.UTF_8)), new ByteArrayInputStream(CLIENT_PRIVATE_KEY_PEM.getBytes(CharsetUtil.UTF_8))) .trustManager(new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8))) .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()).build(); + .sslContextProvider(clientSslContextProvider()) + .protocols(protocols()).ciphers(ciphers())).build(); cb = new Bootstrap(); cb.group(new NioEventLoopGroup()); cb.channel(NioSocketChannel.class); @@ -1610,12 +1700,16 @@ public void testUnwrapBehavior() throws Exception { .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -1790,12 +1884,16 @@ public void testPacketBufferSizeLimit() throws Exception { .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -1829,6 +1927,8 @@ public void testSSLEngineUnwrapNoSslRecord() throws Exception { clientSslCtx = SslContextBuilder .forClient() .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -1857,6 +1957,8 @@ public void testBeginHandshakeAfterEngineClosed() throws SSLException { clientSslCtx = SslContextBuilder .forClient() .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -1881,12 +1983,16 @@ public void testBeginHandshakeCloseOutbound() throws Exception { clientSslCtx = SslContextBuilder .forClient() .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -1928,12 +2034,16 @@ public void testCloseInboundAfterBeginHandshake() throws Exception { clientSslCtx = SslContextBuilder .forClient() .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -1965,12 +2075,16 @@ public void testCloseNotifySequence() throws Exception { .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + // This test only works for non TLSv1.3 for now + .protocols(PROTOCOL_TLS_V1_2) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + // This test only works for non TLSv1.3 for now + .protocols(PROTOCOL_TLS_V1_2) .build(); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -2032,6 +2146,7 @@ public void testCloseNotifySequence() throws Exception { result = server.wrap(empty, encryptedServerToClient); encryptedServerToClient.flip(); + assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); // UNWRAP/WRAP are not expected after this point assertEquals(SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, result.getHandshakeStatus()); @@ -2046,6 +2161,7 @@ public void testCloseNotifySequence() throws Exception { assertTrue(server.isInboundDone()); result = client.unwrap(encryptedServerToClient, plainClientOut); + plainClientOut.flip(); assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); // UNWRAP/WRAP are not expected after this point @@ -2106,12 +2222,16 @@ public void testWrapAfterCloseOutbound() throws Exception { .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -2145,12 +2265,16 @@ public void testMultipleRecordsInOneBufferWithNonZeroPosition() throws Exception .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -2220,12 +2344,16 @@ public void testMultipleRecordsInOneBufferBiggerThenPacketBufferSize() throws Ex .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -2240,21 +2368,35 @@ public void testMultipleRecordsInOneBufferBiggerThenPacketBufferSize() throws Ex int srcLen = plainClientOut.remaining(); SSLEngineResult result; - while (encClientToServer.position() <= server.getSession().getPacketBufferSize()) { + int count = 0; + do { + int plainClientOutPosition = plainClientOut.position(); + int encClientToServerPosition = encClientToServer.position(); result = client.wrap(plainClientOut, encClientToServer); + if (result.getStatus() == Status.BUFFER_OVERFLOW) { + // We did not have enough room to wrap + assertEquals(plainClientOutPosition, plainClientOut.position()); + assertEquals(encClientToServerPosition, encClientToServer.position()); + break; + } assertEquals(SSLEngineResult.Status.OK, result.getStatus()); assertEquals(srcLen, result.bytesConsumed()); assertTrue(result.bytesProduced() > 0); plainClientOut.clear(); - } + ++count; + } while (encClientToServer.position() < server.getSession().getPacketBufferSize()); + + // Check that we were able to wrap multiple times. + assertTrue(count >= 2); encClientToServer.flip(); result = server.unwrap(encClientToServer, plainServerOut); assertEquals(SSLEngineResult.Status.OK, result.getStatus()); assertTrue(result.bytesConsumed() > 0); assertTrue(result.bytesProduced() > 0); + assertTrue(encClientToServer.hasRemaining()); } finally { cert.delete(); cleanupClientSslEngine(client); @@ -2270,12 +2412,16 @@ public void testBufferUnderFlow() throws Exception { .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -2341,12 +2487,16 @@ public void testWrapDoesNotZeroOutSrc() throws Exception { .forClient() .trustManager(cert.cert()) .sslProvider(sslClientProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); serverSslCtx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -2393,6 +2543,8 @@ private void testDisableProtocols(String protocol, String... disabledProtocols) SslContext ctx = SslContextBuilder .forServer(cert.certificate(), cert.privateKey()) .sslProvider(sslServerProvider()) + .protocols(protocols()) + .ciphers(ciphers()) .build(); SSLEngine server = wrapEngine(ctx.newEngine(UnpooledByteBufAllocator.DEFAULT)); @@ -2495,4 +2647,12 @@ protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { protected SSLEngine wrapEngine(SSLEngine engine) { return engine; } + + protected List ciphers() { + return Collections.singletonList(protocolCipherCombo.cipher); + } + + protected String[] protocols() { + return new String[] { protocolCipherCombo.protocol }; + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java index 0a9429e9c1a8..20f2ccbb1459 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java @@ -15,18 +15,19 @@ */ package io.netty.handler.ssl; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.handler.ssl.util.SelfSignedCertificate; import org.junit.Assume; +import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import java.util.Collections; +import static org.junit.Assert.*; + public class SslContextBuilderTest { @Test @@ -79,10 +80,19 @@ public void testInvalidCipherJdk() throws Exception { testInvalidCipher(SslProvider.JDK); } - @Test(expected = SSLException.class) + @Test public void testInvalidCipherOpenSSL() throws Exception { Assume.assumeTrue(OpenSsl.isAvailable()); - testInvalidCipher(SslProvider.OPENSSL); + try { + // This may fail or not depending on the OpenSSL version used + // See https://github.com/openssl/openssl/issues/7196 + testInvalidCipher(SslProvider.OPENSSL); + if (!OpenSsl.versionString().contains("1.1.1")) { + fail(); + } + } catch (SSLException expected) { + // ok + } } private static void testInvalidCipher(SslProvider provider) throws Exception { diff --git a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java index d935cdf683df..ab7de93787c6 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java @@ -124,9 +124,10 @@ public void testCorrectAlert() throws Exception { Assume.assumeTrue(OpenSsl.isAvailable()); SelfSignedCertificate ssc = new SelfSignedCertificate(); - final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(serverProvider) - .trustManager(new SimpleTrustManagerFactory() { + final SslContext sslServerCtx = OpenSslTestUtils.configureProtocolForMutualAuth( + SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) + .sslProvider(serverProvider) + .trustManager(new SimpleTrustManagerFactory() { @Override protected void engineInit(KeyStore keyStore) { } @Override @@ -154,13 +155,13 @@ public X509Certificate[] getAcceptedIssuers() { } } }; } - }).clientAuth(ClientAuth.REQUIRE).build(); + }).clientAuth(ClientAuth.REQUIRE), clientProvider, serverProvider).build(); - final SslContext sslClientCtx = SslContextBuilder.forClient() + final SslContext sslClientCtx = OpenSslTestUtils.configureProtocolForMutualAuth(SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .keyManager(new File(getClass().getResource("test.crt").getFile()), new File(getClass().getResource("test_unencrypted.pem").getFile())) - .sslProvider(clientProvider).build(); + .sslProvider(clientProvider), clientProvider, serverProvider).build(); Channel serverChannel = null; Channel clientChannel = null; diff --git a/handler/src/test/java/io/netty/handler/ssl/SslUtilsTest.java b/handler/src/test/java/io/netty/handler/ssl/SslUtilsTest.java index c1de33dd6e3d..ce9a22d717f8 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslUtilsTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslUtilsTest.java @@ -28,6 +28,7 @@ import static io.netty.handler.ssl.SslUtils.getEncryptedPacketLength; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class SslUtilsTest { @@ -63,4 +64,15 @@ private static SSLEngine newEngine() throws SSLException, NoSuchAlgorithmExcepti engine.beginHandshake(); return engine; } + + @Test + public void testIsTLSv13Cipher() { + assertTrue(SslUtils.isTLSv13Cipher("TLS_AES_128_GCM_SHA256")); + assertTrue(SslUtils.isTLSv13Cipher("TLS_AES_256_GCM_SHA384")); + assertTrue(SslUtils.isTLSv13Cipher("TLS_CHACHA20_POLY1305_SHA256")); + assertTrue(SslUtils.isTLSv13Cipher("TLS_AES_128_CCM_SHA256")); + assertTrue(SslUtils.isTLSv13Cipher("TLS_AES_128_CCM_8_SHA256")); + assertFalse(SslUtils.isTLSv13Cipher("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")); + } + } diff --git a/pom.xml b/pom.xml index 6c42c7977b07..9f7a80b75912 100644 --- a/pom.xml +++ b/pom.xml @@ -241,7 +241,7 @@ fedora netty-tcnative - 2.0.17.Final + 2.0.18.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java index 1a49bde78bbc..8036f081f4a7 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java @@ -138,7 +138,8 @@ public void testSslRenegotiationRejected(ServerBootstrap sb, Bootstrap cb) throw public void initChannel(Channel sch) throws Exception { serverChannel = sch; serverSslHandler = serverCtx.newHandler(sch.alloc()); - + // As we test renegotiation we should use a protocol that support it. + serverSslHandler.engine().setEnabledProtocols(new String[] { "TLSv1.2" }); sch.pipeline().addLast("ssl", serverSslHandler); sch.pipeline().addLast("handler", serverHandler); } @@ -150,7 +151,8 @@ public void initChannel(Channel sch) throws Exception { public void initChannel(Channel sch) throws Exception { clientChannel = sch; clientSslHandler = clientCtx.newHandler(sch.alloc()); - + // As we test renegotiation we should use a protocol that support it. + clientSslHandler.engine().setEnabledProtocols(new String[] { "TLSv1.2" }); sch.pipeline().addLast("ssl", clientSslHandler); sch.pipeline().addLast("handler", clientHandler); } diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java index 7c94b5a6710c..4cdeae98beb8 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java @@ -123,17 +123,33 @@ public String toString() { "autoRead = {5}, useChunkedWriteHandler = {6}, useCompositeByteBuf = {7}") public static Collection data() throws Exception { List serverContexts = new ArrayList(); - serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE).sslProvider(SslProvider.JDK).build()); + serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE) + .sslProvider(SslProvider.JDK) + // As we test renegotiation we should use a protocol that support it. + .protocols("TLSv1.2") + .build()); List clientContexts = new ArrayList(); - clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.JDK).trustManager(CERT_FILE).build()); + clientContexts.add(SslContextBuilder.forClient() + .sslProvider(SslProvider.JDK) + .trustManager(CERT_FILE) + // As we test renegotiation we should use a protocol that support it. + .protocols("TLSv1.2") + .build()); boolean hasOpenSsl = OpenSsl.isAvailable(); if (hasOpenSsl) { serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE) - .sslProvider(SslProvider.OPENSSL).build()); - clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.OPENSSL) - .trustManager(CERT_FILE).build()); + .sslProvider(SslProvider.OPENSSL) + // As we test renegotiation we should use a protocol that support it. + .protocols("TLSv1.2") + .build()); + clientContexts.add(SslContextBuilder.forClient() + .sslProvider(SslProvider.OPENSSL) + .trustManager(CERT_FILE) + // As we test renegotiation we should use a protocol that support it. + .protocols("TLSv1.2") + .build()); } else { logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause()); } diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslSessionReuseTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslSessionReuseTest.java index 4071c8461049..5d0fd0a5e428 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslSessionReuseTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslSessionReuseTest.java @@ -98,7 +98,7 @@ public void testSslSessionReuse() throws Throwable { public void testSslSessionReuse(ServerBootstrap sb, Bootstrap cb) throws Throwable { final ReadAndDiscardHandler sh = new ReadAndDiscardHandler(true, true); final ReadAndDiscardHandler ch = new ReadAndDiscardHandler(false, true); - final String[] protocols = new String[]{ "TLSv1", "TLSv1.1", "TLSv1.2" }; + final String[] protocols = { "TLSv1", "TLSv1.1", "TLSv1.2" }; sb.childHandler(new ChannelInitializer() { @Override From 2109f14c24f90df3f43aee7f3248ac59e6088735 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 17 Oct 2018 19:36:20 +0200 Subject: [PATCH 213/417] When running our testsuite with netty-tcnative-boringssl-static we should use an empty classifier. (#8396) Motivation: We publish an "uber-jar" for netty-tcnative-boringssl-static so we should use it when testing against boringssl. Modifications: Ensure we use empty classifier. Result: Use uber-jar when testing --- docker/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index ceb5a8791c65..65fffba038c6 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -27,7 +27,7 @@ services: test-boringssl-static: <<: *common - command: /bin/bash -cl "./mvnw clean install -Dio.netty.testsuite.badHost=netty.io -Dtcnative.artifactId=netty-tcnative-boringssl-static" + command: /bin/bash -cl "./mvnw clean install -Dio.netty.testsuite.badHost=netty.io -Dtcnative.artifactId=netty-tcnative-boringssl-static -Dtcnative.classifier=" shell: <<: *common From 3543e17967699b89edba7436c932853f9580dd9c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 18 Oct 2018 07:38:03 +0200 Subject: [PATCH 214/417] Ensure OpenSslEngine will not try to call SSL_free multiple times even when constructor throws. (#8399) Motivation: When the constructor of OpenSslEngine threw we could end up to self call SSL_free by ourself and then have the finalizer do the same which may lead to double free-ing and so SIGSEV. Modifications: Just call shutdown() when the constructor throws and so ensure SSL_free is guarded correctly in the finalizer. Result: No more SIGSEV possible. --- .../io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 4ddcc2aefa23..d70c49ab90ed 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -363,7 +363,11 @@ public List getStatusResponses() { // setMode may impact the overhead. calculateMaxWrapOverhead(); } catch (Throwable cause) { - SSL.freeSSL(ssl); + // Call shutdown so we are sure we correctly release all native memory and also guard against the + // case when shutdown() will be called by the finalizer again. If we would call SSL.free(...) directly + // the finalizer may end up calling it again as we would miss to update the DESTROYED_UPDATER. + shutdown(); + PlatformDependent.throwException(cause); } } From f24da67a239da4b9a4f5e97565061006994d2f7a Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 18 Oct 2018 08:22:20 +0200 Subject: [PATCH 215/417] Update jetty-alpn-agent version to support latest JDK 8 release. (#8402) Motivation: We need to update jetty-alpn-agent to be able to run tests with OpenJDK 8u191 Modifications: Update to 2.0.8 Result: Be able to run tests with latest JDK 8 release. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9f7a80b75912..0e66e2461f0c 100644 --- a/pom.xml +++ b/pom.xml @@ -222,7 +222,7 @@ UTF-8 22 1.4.11.Final - 2.0.7 + 2.0.8 "${settings.localRepository}"/org/mortbay/jetty/alpn/jetty-alpn-agent/${jetty.alpnAgent.version}/jetty-alpn-agent-${jetty.alpnAgent.version}.jar -server From 47e4a58d90fa627658f8957d2e984b108139474b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 18 Oct 2018 12:41:30 +0200 Subject: [PATCH 216/417] Explicit specify java version to use to ensure we rebuild image when java version changes. (#8397) Motivation: We should explicit specify the java version to use to ensure docker will rebuild the image once a new java version was released and we specify it. Also we should use openjdk for testing when possible. Modifications: - Explicit specify the java versions to use - Use openjdk when possible. Result: Ensure latest java versions are used during testing --- docker/docker-compose.centos-6.110.yaml | 2 +- docker/docker-compose.centos-6.111.yaml | 2 +- docker/docker-compose.centos-6.112.yaml | 2 +- docker/docker-compose.centos-6.18.yaml | 2 +- docker/docker-compose.centos-6.19.yaml | 2 +- docker/docker-compose.centos-7.110.yaml | 2 +- docker/docker-compose.centos-7.111.yaml | 2 +- docker/docker-compose.centos-7.112.yaml | 2 +- docker/docker-compose.centos-7.18.yaml | 2 +- docker/docker-compose.centos-7.19.yaml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docker/docker-compose.centos-6.110.yaml b/docker/docker-compose.centos-6.110.yaml index 0db4cfd83787..144780de9ec9 100644 --- a/docker/docker-compose.centos-6.110.yaml +++ b/docker/docker-compose.centos-6.110.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "1.10-0" + java_version : "openjdk@1.10.0-2" test: image: netty:centos-6-1.10 diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml index 47ddec15f2ab..12b19d9189ac 100644 --- a/docker/docker-compose.centos-6.111.yaml +++ b/docker/docker-compose.centos-6.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "1.11.0" + java_version : "openjdk@1.11.0-1" test: image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-6.112.yaml b/docker/docker-compose.centos-6.112.yaml index 86a97a5ce824..456d603e8f23 100644 --- a/docker/docker-compose.centos-6.112.yaml +++ b/docker/docker-compose.centos-6.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "openjdk@1.12.0" + java_version : "openjdk@1.12.0-15" test: image: netty:centos-6-1.12 diff --git a/docker/docker-compose.centos-6.18.yaml b/docker/docker-compose.centos-6.18.yaml index 61b2b6edb1c6..43817e5c0e24 100644 --- a/docker/docker-compose.centos-6.18.yaml +++ b/docker/docker-compose.centos-6.18.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "1.8" + java_version : "1.8.192" test: image: netty:centos-6-1.8 diff --git a/docker/docker-compose.centos-6.19.yaml b/docker/docker-compose.centos-6.19.yaml index 861d9d9e7652..93588d6e4e38 100644 --- a/docker/docker-compose.centos-6.19.yaml +++ b/docker/docker-compose.centos-6.19.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "zulu@1.9.0" + java_version : "openjdk@1.9.0-4" test: image: netty:centos-6-1.9 diff --git a/docker/docker-compose.centos-7.110.yaml b/docker/docker-compose.centos-7.110.yaml index d2004a1b1d82..7f165208e1e0 100644 --- a/docker/docker-compose.centos-7.110.yaml +++ b/docker/docker-compose.centos-7.110.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "1.10-0" + java_version : "openjdk@1.10.0-2" test: image: netty:centos-7-1.10 diff --git a/docker/docker-compose.centos-7.111.yaml b/docker/docker-compose.centos-7.111.yaml index 517f9334429a..f80a7355e701 100644 --- a/docker/docker-compose.centos-7.111.yaml +++ b/docker/docker-compose.centos-7.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "1.11.0" + java_version : "openjdk@1.11.0-1" test: image: netty:centos-7-1.11 diff --git a/docker/docker-compose.centos-7.112.yaml b/docker/docker-compose.centos-7.112.yaml index 627e8d34a0e3..73bae6fdec40 100644 --- a/docker/docker-compose.centos-7.112.yaml +++ b/docker/docker-compose.centos-7.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "openjdk@1.12.0" + java_version : "openjdk@1.12.0-15" test: image: netty:centos-7-1.12 diff --git a/docker/docker-compose.centos-7.18.yaml b/docker/docker-compose.centos-7.18.yaml index df3aa0040aee..20008caa04f9 100644 --- a/docker/docker-compose.centos-7.18.yaml +++ b/docker/docker-compose.centos-7.18.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "1.8" + java_version : "1.8.192" test: image: netty:centos-7-1.8 diff --git a/docker/docker-compose.centos-7.19.yaml b/docker/docker-compose.centos-7.19.yaml index 3b6187538904..5f1af4f11e79 100644 --- a/docker/docker-compose.centos-7.19.yaml +++ b/docker/docker-compose.centos-7.19.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "zulu@1.9.0" + java_version : "openjdk@1.9.0-4" test: image: netty:centos-7-1.9 From 201e984cb3995d59cf8254f851f0ffb9090c2fea Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 18 Oct 2018 13:50:12 +0200 Subject: [PATCH 217/417] Allow to use TLSv1.3 with netty-tcnative withe java versions prior to 11. (#8394) Motivation: At the moment it's only possible to use TLSv1.3 with netty-tcnative if Java 11 is used. It should be possible to do so even with Java 8, 9 and 10. Modification: Add a workaround to be able to use TLSv1.3 also when using Java version prior to Java 11 and the default X509ExtendedTrustManager is used. Result: Be able to use TLSv1.3 also with past versions of Java. --- .../handler/ssl/ExtendedOpenSslSession.java | 3 +- .../java/io/netty/handler/ssl/OpenSsl.java | 33 +- ...OpenSslTlsv13X509ExtendedTrustManager.java | 498 ++++++++++++++++++ .../ReferenceCountedOpenSslClientContext.java | 2 +- .../ReferenceCountedOpenSslServerContext.java | 2 +- .../java/io/netty/handler/ssl/SslUtils.java | 3 +- .../netty/handler/ssl/OpenSslEngineTest.java | 2 +- .../io/netty/handler/ssl/OpenSslTest.java | 6 +- .../io/netty/handler/ssl/SSLEngineTest.java | 1 + 9 files changed, 519 insertions(+), 31 deletions(-) create mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java diff --git a/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java b/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java index 69a6125d172a..184845a9fe1f 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java +++ b/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java @@ -42,7 +42,6 @@ abstract class ExtendedOpenSslSession extends ExtendedSSLSession implements Open private final OpenSslSession wrapped; ExtendedOpenSslSession(OpenSslSession wrapped) { - assert !(wrapped instanceof ExtendedSSLSession); this.wrapped = wrapped; } @@ -153,7 +152,7 @@ public final String getCipherSuite() { } @Override - public final String getProtocol() { + public String getProtocol() { return wrapped.getProtocol(); } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index 955404b25e14..073c855a4a6d 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -31,7 +31,6 @@ import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; -import javax.net.ssl.SSLException; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; @@ -40,16 +39,7 @@ import java.util.List; import java.util.Set; -import static io.netty.handler.ssl.SslUtils.DEFAULT_CIPHER_SUITES; -import static io.netty.handler.ssl.SslUtils.addIfSupported; -import static io.netty.handler.ssl.SslUtils.useFallbackCiphersIfDefaultIsEmpty; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2_HELLO; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V3; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_3; +import static io.netty.handler.ssl.SslUtils.*; /** * Tells if {@code netty-tcnative} and its OpenSSL support @@ -154,14 +144,13 @@ public final class OpenSsl { long certBio = 0; SelfSignedCertificate cert = null; try { - if (PlatformDependent.javaVersion() >= 11) { - try { - SSLContext.setCipherSuite(sslCtx, TLSV13_CIPHERS, true); - tlsv13Supported = true; - } catch (Exception ignore) { - tlsv13Supported = false; - } + try { + SSLContext.setCipherSuite(sslCtx, TLSV13_CIPHERS, true); + tlsv13Supported = true; + } catch (Exception ignore) { + tlsv13Supported = false; } + SSLContext.setCipherSuite(sslCtx, "ALL", false); final long ssl = SSL.newSSL(sslCtx, true); @@ -231,6 +220,8 @@ public Boolean run() { } addIfSupported(availableJavaCipherSuites, defaultCiphers, DEFAULT_CIPHER_SUITES); + addIfSupported(availableJavaCipherSuites, defaultCiphers, TLSV13_CIPHER_SUITES); + useFallbackCiphersIfDefaultIsEmpty(defaultCiphers, availableJavaCipherSuites); DEFAULT_CIPHERS = Collections.unmodifiableList(defaultCiphers); @@ -266,11 +257,7 @@ public Boolean run() { } // This is only supported by java11 and later. - if (tlsv13Supported && doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_3, SSL.SSL_OP_NO_TLSv1_3) - && PlatformDependent.javaVersion() >= 11) { - // We can only support TLS1.3 when using Java 11 or higher as otherwise it will fail to create the - // internal instance of an sun.security.ssl.ProtocolVersion as can not parse the version string :/ - // See http://mail.openjdk.java.net/pipermail/security-dev/2018-September/018242.html + if (tlsv13Supported && doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_3, SSL.SSL_OP_NO_TLSv1_3)) { protocols.add(PROTOCOL_TLS_V1_3); TLSV13_SUPPORTED = true; } else { diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java new file mode 100644 index 000000000000..e145c05c0875 --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java @@ -0,0 +1,498 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import io.netty.util.internal.EmptyArrays; +import io.netty.util.internal.PlatformDependent; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSessionContext; +import javax.net.ssl.X509ExtendedTrustManager; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.security.Principal; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.List; + +/** + * Provide a way to use {@code TLSv1.3} with Java versions prior to 11 by adding a + * = 7 && session instanceof ExtendedOpenSslSession) { + final ExtendedOpenSslSession extendedOpenSslSession = (ExtendedOpenSslSession) session; + return new ExtendedOpenSslSession(extendedOpenSslSession) { + @Override + public List getRequestedServerNames() { + return extendedOpenSslSession.getRequestedServerNames(); + } + + @Override + public String[] getPeerSupportedSignatureAlgorithms() { + return extendedOpenSslSession.getPeerSupportedSignatureAlgorithms(); + } + + @Override + public String getProtocol() { + return SslUtils.PROTOCOL_TLS_V1_2; + } + }; + } else { + return new SSLSession() { + @Override + public byte[] getId() { + return session.getId(); + } + + @Override + public SSLSessionContext getSessionContext() { + return session.getSessionContext(); + } + + @Override + public long getCreationTime() { + return session.getCreationTime(); + } + + @Override + public long getLastAccessedTime() { + return session.getLastAccessedTime(); + } + + @Override + public void invalidate() { + session.invalidate(); + } + + @Override + public boolean isValid() { + return session.isValid(); + } + + @Override + public void putValue(String s, Object o) { + session.putValue(s, o); + } + + @Override + public Object getValue(String s) { + return session.getValue(s); + } + + @Override + public void removeValue(String s) { + session.removeValue(s); + } + + @Override + public String[] getValueNames() { + return session.getValueNames(); + } + + @Override + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + return session.getPeerCertificates(); + } + + @Override + public Certificate[] getLocalCertificates() { + return session.getLocalCertificates(); + } + + @Override + public javax.security.cert.X509Certificate[] getPeerCertificateChain() + throws SSLPeerUnverifiedException { + return session.getPeerCertificateChain(); + } + + @Override + public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { + return session.getPeerPrincipal(); + } + + @Override + public Principal getLocalPrincipal() { + return session.getLocalPrincipal(); + } + + @Override + public String getCipherSuite() { + return session.getCipherSuite(); + } + + @Override + public String getProtocol() { + return SslUtils.PROTOCOL_TLS_V1_2; + } + + @Override + public String getPeerHost() { + return session.getPeerHost(); + } + + @Override + public int getPeerPort() { + return session.getPeerPort(); + } + + @Override + public int getPacketBufferSize() { + return session.getPacketBufferSize(); + } + + @Override + public int getApplicationBufferSize() { + return session.getApplicationBufferSize(); + } + }; + } + } + }; + } + return engine; + } + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, final String s, SSLEngine sslEngine) + throws CertificateException { + tm.checkClientTrusted(x509Certificates, s, wrapEngine(sslEngine)); + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) + throws CertificateException { + tm.checkServerTrusted(x509Certificates, s, wrapEngine(sslEngine)); + } + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + tm.checkClientTrusted(x509Certificates, s); + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + tm.checkServerTrusted(x509Certificates, s); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return tm.getAcceptedIssuers(); + } + + private static final class DummySSLEngine extends SSLEngine { + + private final boolean client; + + DummySSLEngine(boolean client) { + this.client = client; + } + + @Override + public SSLSession getHandshakeSession() { + return new SSLSession() { + @Override + public byte[] getId() { + return EmptyArrays.EMPTY_BYTES; + } + + @Override + public SSLSessionContext getSessionContext() { + return null; + } + + @Override + public long getCreationTime() { + return 0; + } + + @Override + public long getLastAccessedTime() { + return 0; + } + + @Override + public void invalidate() { + // NOOP + } + + @Override + public boolean isValid() { + return false; + } + + @Override + public void putValue(String s, Object o) { + // NOOP + } + + @Override + public Object getValue(String s) { + return null; + } + + @Override + public void removeValue(String s) { + // NOOP + } + + @Override + public String[] getValueNames() { + return EmptyArrays.EMPTY_STRINGS; + } + + @Override + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + return EmptyArrays.EMPTY_CERTIFICATES; + } + + @Override + public Certificate[] getLocalCertificates() { + return EmptyArrays.EMPTY_CERTIFICATES; + } + + @Override + public javax.security.cert.X509Certificate[] getPeerCertificateChain() + throws SSLPeerUnverifiedException { + return EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES; + } + + @Override + public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { + return null; + } + + @Override + public Principal getLocalPrincipal() { + return null; + } + + @Override + public String getCipherSuite() { + return null; + } + + @Override + public String getProtocol() { + return SslUtils.PROTOCOL_TLS_V1_3; + } + + @Override + public String getPeerHost() { + return null; + } + + @Override + public int getPeerPort() { + return 0; + } + + @Override + public int getPacketBufferSize() { + return 0; + } + + @Override + public int getApplicationBufferSize() { + return 0; + } + }; + } + + @Override + public SSLEngineResult wrap(ByteBuffer[] byteBuffers, int i, int i1, ByteBuffer byteBuffer) + throws SSLException { + throw new UnsupportedOperationException(); + } + + @Override + public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers, int i, int i1) + throws SSLException { + throw new UnsupportedOperationException(); + } + + @Override + public Runnable getDelegatedTask() { + return null; + } + + @Override + public void closeInbound() throws SSLException { + // NOOP + } + + @Override + public boolean isInboundDone() { + return true; + } + + @Override + public void closeOutbound() { + // NOOP + } + + @Override + public boolean isOutboundDone() { + return true; + } + + @Override + public String[] getSupportedCipherSuites() { + return EmptyArrays.EMPTY_STRINGS; + } + + @Override + public String[] getEnabledCipherSuites() { + return EmptyArrays.EMPTY_STRINGS; + } + + @Override + public void setEnabledCipherSuites(String[] strings) { + // NOOP + } + + @Override + public String[] getSupportedProtocols() { + return new String[] { SslUtils.PROTOCOL_TLS_V1_3 }; + } + + @Override + public String[] getEnabledProtocols() { + return new String[] { SslUtils.PROTOCOL_TLS_V1_3 }; + } + + @Override + public void setEnabledProtocols(String[] strings) { + // NOOP + } + + @Override + public SSLSession getSession() { + return getHandshakeSession(); + } + + @Override + public void beginHandshake() throws SSLException { + // NOOP + } + + @Override + public HandshakeStatus getHandshakeStatus() { + return HandshakeStatus.NEED_TASK; + } + + @Override + public void setUseClientMode(boolean b) { + // NOOP + } + + @Override + public boolean getUseClientMode() { + return client; + } + + @Override + public void setNeedClientAuth(boolean b) { + // NOOP + } + + @Override + public boolean getNeedClientAuth() { + return false; + } + + @Override + public void setWantClientAuth(boolean b) { + // NOOP + } + + @Override + public boolean getWantClientAuth() { + return false; + } + + @Override + public void setEnableSessionCreation(boolean b) { + // NOOP + } + + @Override + public boolean getEnableSessionCreation() { + return false; + } + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java index 4972905baafa..f508971f014d 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java @@ -233,7 +233,7 @@ private static final class ExtendedTrustManagerVerifyCallback extends AbstractCe ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509ExtendedTrustManager manager) { super(engineMap); - this.manager = manager; + this.manager = OpenSslTlsv13X509ExtendedTrustManager.wrap(manager, true); } @Override diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java index 1dbae15dd3ce..e901aebb3833 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java @@ -239,7 +239,7 @@ private static final class ExtendedTrustManagerVerifyCallback extends AbstractCe ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509ExtendedTrustManager manager) { super(engineMap); - this.manager = manager; + this.manager = OpenSslTlsv13X509ExtendedTrustManager.wrap(manager, false); } @Override diff --git a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java b/handler/src/main/java/io/netty/handler/ssl/SslUtils.java index f1f000fa8fd4..492c4f2b7d95 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslUtils.java @@ -97,10 +97,11 @@ final class SslUtils { static final String[] DEFAULT_CIPHER_SUITES; static final String[] DEFAULT_TLSV13_CIPHER_SUITES; + static final String[] TLSV13_CIPHER_SUITES = { "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" }; static { if (PlatformDependent.javaVersion() >= 11) { - DEFAULT_TLSV13_CIPHER_SUITES = new String[] { "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" }; + DEFAULT_TLSV13_CIPHER_SUITES = TLSV13_CIPHER_SUITES; } else { DEFAULT_TLSV13_CIPHER_SUITES = EmptyArrays.EMPTY_STRINGS; } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index 630de226d6d9..1ae88ebdfbe7 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -73,7 +73,7 @@ public static Collection data() { for (BufferType type: BufferType.values()) { params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); - if (PlatformDependent.javaVersion() >= 11 && OpenSsl.isTlsv13Supported()) { + if (OpenSsl.isTlsv13Supported()) { params.add(new Object[] { type, ProtocolCipherCombo.tlsv13() }); } } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslTest.java index 4fd849109977..0a9d19db6585 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslTest.java @@ -22,7 +22,9 @@ public class OpenSslTest { @Test public void testDefaultCiphers() { - Assert.assertTrue( - OpenSsl.DEFAULT_CIPHERS.size() <= SslUtils.DEFAULT_CIPHER_SUITES.length); + if (!OpenSsl.isTlsv13Supported()) { + Assert.assertTrue( + OpenSsl.DEFAULT_CIPHERS.size() <= SslUtils.DEFAULT_CIPHER_SUITES.length); + } } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 0094c92d3ec1..fd8c9b8fc3c1 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -1201,6 +1201,7 @@ public void testSSLSessionId() throws Exception { @Test(timeout = 30000) public void clientInitiatedRenegotiationWithFatalAlertDoesNotInfiniteLoopServer() throws CertificateException, SSLException, InterruptedException, ExecutionException { + Assume.assumeTrue(PlatformDependent.javaVersion() >= 11); final SelfSignedCertificate ssc = new SelfSignedCertificate(); serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(sslServerProvider()) From a6f807dd683243aa388aad0bfc9fcb20f3b1edce Mon Sep 17 00:00:00 2001 From: Christian Lang Date: Thu, 18 Oct 2018 14:55:30 +0300 Subject: [PATCH 218/417] Fix context and window sizes sides. (#8395) Motivation: As mentioned in RFC 7692 : The "server_no_context_takeover" Extension Parameter should be used on server side for compression and on client side for decompression. The "client_no_context_takeover" Extension Parameter should be used on client side for compression and on server side for decompression. Right now, in PerMessageDeflateClientExtensionHandshaker, the decoder uses clientNoContext instead of serverNoContext and the encoder uses serverNoContext instead of clientNoContext. The same inversion is present in PerMessageDeflateServerExtensionHandshaker: the decoder uses serverNoContext instead of clientNoContext, while the encoder uses serverNoContext instead of clientNoContext. Besides the context inversion, the sliding window sizes seem to be inversed as well. Modification: Inverse clientNoContext with serverNoContext and clientWindowSize with serverWindowSize for both the Decoder and Encoder in PerMessageDeflateServerExtensionHandshaker and PerMessageDeflateClientExtensionHandshaker. Result: This fixes the decompression fail in the case that one of the contexts is set and the other one is not. --- ...ssageDeflateClientExtensionHandshaker.java | 4 +- ...ssageDeflateServerExtensionHandshaker.java | 4 +- .../PerFrameDeflateDecoderTest.java | 16 +++-- ...eDeflateClientExtensionHandshakerTest.java | 67 ++++++++++++++++++- 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshaker.java index ef3dfe7245ea..ddebaaa92f04 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshaker.java @@ -186,12 +186,12 @@ public PermessageDeflateExtension(boolean serverNoContext, int serverWindowSize, @Override public WebSocketExtensionEncoder newExtensionEncoder() { - return new PerMessageDeflateEncoder(compressionLevel, serverWindowSize, serverNoContext); + return new PerMessageDeflateEncoder(compressionLevel, clientWindowSize, clientNoContext); } @Override public WebSocketExtensionDecoder newExtensionDecoder() { - return new PerMessageDeflateDecoder(clientNoContext); + return new PerMessageDeflateDecoder(serverNoContext); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshaker.java index 738b6f927d40..0bf0162e8a0b 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshaker.java @@ -167,12 +167,12 @@ public int rsv() { @Override public WebSocketExtensionEncoder newExtensionEncoder() { - return new PerMessageDeflateEncoder(compressionLevel, clientWindowSize, clientNoContext); + return new PerMessageDeflateEncoder(compressionLevel, serverWindowSize, serverNoContext); } @Override public WebSocketExtensionDecoder newExtensionDecoder() { - return new PerMessageDeflateDecoder(serverNoContext); + return new PerMessageDeflateDecoder(clientNoContext); } @Override diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateDecoderTest.java index e59d38028708..67baa5151f12 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateDecoderTest.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec.http.websocketx.extensions.compression; +import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension.RSV1; +import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension.RSV3; import static org.junit.Assert.*; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -22,6 +24,7 @@ import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension; import java.util.Arrays; @@ -47,7 +50,7 @@ public void testCompressedFrame() { ByteBuf compressedPayload = encoderChannel.readOutbound(); BinaryWebSocketFrame compressedFrame = new BinaryWebSocketFrame(true, - WebSocketExtension.RSV1 | WebSocketExtension.RSV3, + RSV1 | RSV3, compressedPayload.slice(0, compressedPayload.readableBytes() - 4)); // execute @@ -58,7 +61,7 @@ public void testCompressedFrame() { assertNotNull(uncompressedFrame); assertNotNull(uncompressedFrame.content()); assertTrue(uncompressedFrame instanceof BinaryWebSocketFrame); - assertEquals(WebSocketExtension.RSV3, uncompressedFrame.rsv()); + assertEquals(RSV3, uncompressedFrame.rsv()); assertEquals(300, uncompressedFrame.content().readableBytes()); byte[] finalPayload = new byte[300]; @@ -76,7 +79,7 @@ public void testNormalFrame() { random.nextBytes(payload); BinaryWebSocketFrame frame = new BinaryWebSocketFrame(true, - WebSocketExtension.RSV3, Unpooled.wrappedBuffer(payload)); + RSV3, Unpooled.wrappedBuffer(payload)); // execute decoderChannel.writeInbound(frame); @@ -86,7 +89,7 @@ public void testNormalFrame() { assertNotNull(newFrame); assertNotNull(newFrame.content()); assertTrue(newFrame instanceof BinaryWebSocketFrame); - assertEquals(WebSocketExtension.RSV3, newFrame.rsv()); + assertEquals(RSV3, newFrame.rsv()); assertEquals(300, newFrame.content().readableBytes()); byte[] finalPayload = new byte[300]; @@ -105,7 +108,7 @@ public void testCompressedEmptyFrame() { encoderChannel.writeOutbound(Unpooled.EMPTY_BUFFER); ByteBuf compressedPayload = encoderChannel.readOutbound(); BinaryWebSocketFrame compressedFrame = - new BinaryWebSocketFrame(true, WebSocketExtension.RSV1 | WebSocketExtension.RSV3, compressedPayload); + new BinaryWebSocketFrame(true, RSV1 | RSV3, compressedPayload); // execute decoderChannel.writeInbound(compressedFrame); @@ -115,9 +118,8 @@ public void testCompressedEmptyFrame() { assertNotNull(uncompressedFrame); assertNotNull(uncompressedFrame.content()); assertTrue(uncompressedFrame instanceof BinaryWebSocketFrame); - assertEquals(WebSocketExtension.RSV3, uncompressedFrame.rsv()); + assertEquals(RSV3, uncompressedFrame.rsv()); assertEquals(0, uncompressedFrame.content().readableBytes()); uncompressedFrame.release(); } - } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshakerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshakerTest.java index 7b0fa4a3d3a9..8ed3a7c25f18 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshakerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshakerTest.java @@ -15,11 +15,15 @@ */ package io.netty.handler.codec.http.websocketx.extensions.compression; +import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension.RSV1; import static io.netty.handler.codec.http.websocketx.extensions.compression. PerMessageDeflateServerExtensionHandshaker.*; import static org.junit.Assert.*; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.compression.ZlibCodecFactory; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtension; import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData; @@ -66,7 +70,7 @@ public void testNormalHandshake() { new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, Collections.emptyMap())); assertNotNull(extension); - assertEquals(WebSocketClientExtension.RSV1, extension.rsv()); + assertEquals(RSV1, extension.rsv()); assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); } @@ -92,7 +96,7 @@ public void testCustomHandshake() { // test assertNotNull(extension); - assertEquals(WebSocketClientExtension.RSV1, extension.rsv()); + assertEquals(RSV1, extension.rsv()); assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); @@ -107,7 +111,7 @@ public void testCustomHandshake() { // test assertNotNull(extension); - assertEquals(WebSocketClientExtension.RSV1, extension.rsv()); + assertEquals(RSV1, extension.rsv()); assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); @@ -121,4 +125,61 @@ public void testCustomHandshake() { // test assertNull(extension); } + + @Test + public void testDecoderNoClientContext() { + PerMessageDeflateClientExtensionHandshaker handshaker = + new PerMessageDeflateClientExtensionHandshaker(6, true, MAX_WINDOW_SIZE, true, false); + + byte[] firstPayload = new byte[] { + 76, -50, -53, 10, -62, 48, 20, 4, -48, 95, 41, 89, -37, 36, 77, 90, 31, -39, 41, -72, 112, 33, -120, 20, + 20, 119, -79, 70, 123, -95, 121, -48, 92, -116, 80, -6, -17, -58, -99, -37, -31, 12, 51, 19, 1, -9, -12, + 68, -111, -117, 25, 58, 111, 77, -127, -66, -64, -34, 20, 59, -64, -29, -2, 90, -100, -115, 30, 16, 114, + -68, 61, 29, 40, 89, -112, -73, 25, 35, 120, -105, -67, -32, -43, -70, -84, 120, -55, 69, 43, -124, 106, + -92, 18, -110, 114, -50, 111, 25, -3, 10, 17, -75, 13, 127, -84, 106, 90, -66, 84, -75, 84, 53, -89, + -75, 92, -3, -40, -61, 119, 49, -117, 30, 49, 68, -59, 88, 74, -119, -34, 1, -83, -7, -48, 124, -124, + -23, 16, 88, -118, 121, 54, -53, 1, 44, 32, 81, 19, 25, -115, -43, -32, -64, -67, -120, -110, -101, 121, + -2, 2 + }; + + byte[] secondPayload = new byte[] { + -86, 86, 42, 46, 77, 78, 78, 45, 6, 26, 83, 82, 84, -102, -86, 3, -28, 38, 21, 39, 23, 101, 38, -91, 2, + -51, -51, 47, 74, 73, 45, 114, -54, -49, -49, -10, 49, -78, -118, 112, 10, 9, 13, 118, 1, -102, 84, + -108, 90, 88, 10, 116, 27, -56, -84, 124, -112, -13, 16, 26, 116, -108, 18, -117, -46, -127, 6, 69, 99, + -45, 24, 91, 91, 11, 0 + }; + + Map parameters = Collections.singletonMap(CLIENT_NO_CONTEXT, null); + + WebSocketClientExtension extension = handshaker.handshakeExtension( + new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); + assertNotNull(extension); + + EmbeddedChannel decoderChannel = new EmbeddedChannel(extension.newExtensionDecoder()); + assertTrue( + decoderChannel.writeInbound(new TextWebSocketFrame(true, RSV1, Unpooled.copiedBuffer(firstPayload)))); + TextWebSocketFrame firstFrameDecompressed = decoderChannel.readInbound(); + assertTrue( + decoderChannel.writeInbound(new TextWebSocketFrame(true, RSV1, Unpooled.copiedBuffer(secondPayload)))); + TextWebSocketFrame secondFrameDecompressed = decoderChannel.readInbound(); + + assertNotNull(firstFrameDecompressed); + assertNotNull(firstFrameDecompressed.content()); + assertTrue(firstFrameDecompressed instanceof TextWebSocketFrame); + assertEquals(firstFrameDecompressed.text(), + "{\"info\":\"Welcome to the BitMEX Realtime API.\",\"version\"" + + ":\"2018-10-02T22:53:23.000Z\",\"timestamp\":\"2018-10-15T06:43:40.437Z\"," + + "\"docs\":\"https://www.bitmex.com/app/wsAPI\",\"limit\":{\"remaining\":39}}"); + assertTrue(firstFrameDecompressed.release()); + + assertNotNull(secondFrameDecompressed); + assertNotNull(secondFrameDecompressed.content()); + assertTrue(secondFrameDecompressed instanceof TextWebSocketFrame); + assertEquals(secondFrameDecompressed.text(), + "{\"success\":true,\"subscribe\":\"orderBookL2:XBTUSD\"," + + "\"request\":{\"op\":\"subscribe\",\"args\":[\"orderBookL2:XBTUSD\"]}}"); + assertTrue(secondFrameDecompressed.release()); + + assertFalse(decoderChannel.finish()); + } } From 3a4a0432d309dd17de86bd1cf4742030174fc190 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 18 Oct 2018 17:33:21 +0200 Subject: [PATCH 219/417] Fix broken testsuite-shading when using with netty-tcnative-boringssl-static (#8404) Motivation: 2109f14c24f90df3f43aee7f3248ac59e6088735 corrected how we run the testsuite with boringssl-static but missed to also adjust the testsuite-shading configuration which lead to test failures. Modifications: Correctly compose the native lib name when no classifier is used. Result: Testsuite passes again. --- docker/docker-compose.yaml | 2 +- testsuite-shading/pom.xml | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 65fffba038c6..3b8d5d0e223e 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -27,7 +27,7 @@ services: test-boringssl-static: <<: *common - command: /bin/bash -cl "./mvnw clean install -Dio.netty.testsuite.badHost=netty.io -Dtcnative.artifactId=netty-tcnative-boringssl-static -Dtcnative.classifier=" + command: /bin/bash -cl "./mvnw clean install -Dio.netty.testsuite.badHost=netty.io -Dxml.skip=true -Dtcnative.artifactId=netty-tcnative-boringssl-static -Dtcnative.classifier=" shell: <<: *common diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index a19adabeacda..23ccd1d490ff 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -74,7 +74,6 @@ netty_transport_native_kqueue_${os.detected.arch}.jnilib - netty_tcnative.jnilib @@ -168,6 +167,10 @@ + + + + @@ -209,7 +212,6 @@ netty_transport_native_epoll_${os.detected.arch}.so - netty_tcnative.so @@ -303,6 +305,10 @@ + + + + From 69545aedc444a380729c3b4cf441cf5b438f939d Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 19 Oct 2018 08:05:22 +0200 Subject: [PATCH 220/417] CompositeByteBuf.decompose(...) does not correctly slice content. (#8403) Motivation: CompositeByteBuf.decompose(...) did not correctly slice the content and so produced an incorrect representation of the data. Modifications: - Rewrote implementation to fix bug and also improved it to reduce GC - Add unit tests. Result: Fixes https://github.com/netty/netty/issues/8400. --- .../io/netty/buffer/CompositeByteBuf.java | 46 ++++++++----------- .../buffer/AbstractCompositeByteBufTest.java | 42 +++++++++++++++++ 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 85160c79a6e6..f987e48681c4 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -526,39 +526,31 @@ public List decompose(int offset, int length) { } int componentId = toComponentIndex(offset); - List slice = new ArrayList(components.size()); - + int bytesToSlice = length; // The first component Component firstC = components.get(componentId); - ByteBuf first = firstC.buf.duplicate(); - first.readerIndex(offset - firstC.offset); + int firstBufOffset = offset - firstC.offset; - ByteBuf buf = first; - int bytesToSlice = length; - do { - int readableBytes = buf.readableBytes(); - if (bytesToSlice <= readableBytes) { - // Last component - buf.writerIndex(buf.readerIndex() + bytesToSlice); - slice.add(buf); - break; - } else { - // Not the last component - slice.add(buf); - bytesToSlice -= readableBytes; - componentId ++; + ByteBuf slice = firstC.buf.slice(firstBufOffset + firstC.buf.readerIndex(), + Math.min(firstC.length - firstBufOffset, bytesToSlice)); + bytesToSlice -= slice.readableBytes(); - // Fetch the next component. - buf = components.get(componentId).buf.duplicate(); - } - } while (bytesToSlice > 0); - - // Slice all components because only readable bytes are interesting. - for (int i = 0; i < slice.size(); i ++) { - slice.set(i, slice.get(i).slice()); + if (bytesToSlice == 0) { + return Collections.singletonList(slice); } - return slice; + List sliceList = new ArrayList(components.size() - componentId); + sliceList.add(slice); + + // Add all the slices until there is nothing more left and then return the List. + do { + Component component = components.get(++componentId); + slice = component.buf.slice(component.buf.readerIndex(), Math.min(component.length, bytesToSlice)); + bytesToSlice -= slice.readableBytes(); + sliceList.add(slice); + } while (bytesToSlice > 0); + + return sliceList; } @Override diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java index 22ca0546aa99..03e55913a069 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java @@ -16,6 +16,7 @@ package io.netty.buffer; import io.netty.util.ReferenceCountUtil; +import io.netty.util.internal.PlatformDependent; import org.junit.Assume; import org.junit.Test; @@ -1136,4 +1137,45 @@ private void testAllocatorIsSameWhenCopy(boolean withIndexAndLength) { buffer.release(); copy.release(); } + + @Test + public void testDecomposeMultiple() { + testDecompose(150, 500, 3); + } + + @Test + public void testDecomposeOne() { + testDecompose(310, 50, 1); + } + + @Test + public void testDecomposeNone() { + testDecompose(310, 0, 0); + } + + private static void testDecompose(int offset, int length, int expectedListSize) { + byte[] bytes = new byte[1024]; + PlatformDependent.threadLocalRandom().nextBytes(bytes); + ByteBuf buf = wrappedBuffer(bytes); + + CompositeByteBuf composite = compositeBuffer(); + composite.addComponents(true, + buf.retainedSlice(100, 200), + buf.retainedSlice(300, 400), + buf.retainedSlice(700, 100)); + + ByteBuf slice = composite.slice(offset, length); + List bufferList = composite.decompose(offset, length); + assertEquals(expectedListSize, bufferList.size()); + ByteBuf wrapped = wrappedBuffer(bufferList.toArray(new ByteBuf[0])); + + assertEquals(slice, wrapped); + composite.release(); + buf.release(); + + for (ByteBuf buffer: bufferList) { + assertEquals(0, buffer.refCnt()); + } + } + } From 87ec2f882af36a4f14e2a3e3f1e2346bad848e7d Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 19 Oct 2018 14:00:13 +0200 Subject: [PATCH 221/417] Reduce overhead by ByteBufUtil.decodeString(...) which is used by `AbstractByteBuf.toString(...)` and `AbstractByteBuf.getCharSequence(...)` (#8388) Motivation: Our current implementation that is used for toString(Charset) operations on AbstractByteBuf implementation is quite slow as it does a lot of uncessary memory copies. We should just use new String(...) as it has a lot of optimizations to handle these cases. Modifications: Rewrite ByteBufUtil.decodeString(...) to use new String(...) Result: Less overhead for toString(Charset) operations. Benchmark (charsetName) (direct) (size) Mode Cnt Score Error Units ByteBufUtilDecodeStringBenchmark.decodeString US-ASCII false 8 thrpt 20 22401645.093 ? 4671452.479 ops/s ByteBufUtilDecodeStringBenchmark.decodeString US-ASCII false 64 thrpt 20 23678483.384 ? 3749164.446 ops/s ByteBufUtilDecodeStringBenchmark.decodeString US-ASCII true 8 thrpt 20 15731142.651 ? 3782931.591 ops/s ByteBufUtilDecodeStringBenchmark.decodeString US-ASCII true 64 thrpt 20 16244232.229 ? 1886259.658 ops/s ByteBufUtilDecodeStringBenchmark.decodeString UTF-8 false 8 thrpt 20 25983680.959 ? 5045782.289 ops/s ByteBufUtilDecodeStringBenchmark.decodeString UTF-8 false 64 thrpt 20 26235589.339 ? 2867004.950 ops/s ByteBufUtilDecodeStringBenchmark.decodeString UTF-8 true 8 thrpt 20 18499027.808 ? 4784684.268 ops/s ByteBufUtilDecodeStringBenchmark.decodeString UTF-8 true 64 thrpt 20 16825286.141 ? 1008712.342 ops/s ByteBufUtilDecodeStringBenchmark.decodeString UTF-16 false 8 thrpt 20 5789879.092 ? 1201786.359 ops/s ByteBufUtilDecodeStringBenchmark.decodeString UTF-16 false 64 thrpt 20 2173243.225 ? 417809.341 ops/s ByteBufUtilDecodeStringBenchmark.decodeString UTF-16 true 8 thrpt 20 5035583.011 ? 1001978.854 ops/s ByteBufUtilDecodeStringBenchmark.decodeString UTF-16 true 64 thrpt 20 2162345.301 ? 402410.408 ops/s ByteBufUtilDecodeStringBenchmark.decodeString ISO-8859-1 false 8 thrpt 20 30039052.376 ? 6539111.622 ops/s ByteBufUtilDecodeStringBenchmark.decodeString ISO-8859-1 false 64 thrpt 20 31414163.515 ? 2096710.526 ops/s ByteBufUtilDecodeStringBenchmark.decodeString ISO-8859-1 true 8 thrpt 20 19538587.855 ? 4639115.572 ops/s ByteBufUtilDecodeStringBenchmark.decodeString ISO-8859-1 true 64 thrpt 20 19467839.722 ? 1672687.213 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld US-ASCII false 8 thrpt 20 10787326.745 ? 1034197.864 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld US-ASCII false 64 thrpt 20 7129801.930 ? 1363019.209 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld US-ASCII true 8 thrpt 20 9002529.605 ? 2017642.445 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld US-ASCII true 64 thrpt 20 3860192.352 ? 826218.738 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld UTF-8 false 8 thrpt 20 10532838.027 ? 2151743.968 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld UTF-8 false 64 thrpt 20 7185554.597 ? 1387685.785 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld UTF-8 true 8 thrpt 20 7352253.316 ? 1333823.850 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld UTF-8 true 64 thrpt 20 2825578.707 ? 349701.156 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld UTF-16 false 8 thrpt 20 7277446.665 ? 1447034.346 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld UTF-16 false 64 thrpt 20 2445929.579 ? 562816.641 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld UTF-16 true 8 thrpt 20 6201174.401 ? 1236137.786 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld UTF-16 true 64 thrpt 20 2310674.973 ? 525587.959 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld ISO-8859-1 false 8 thrpt 20 11142625.392 ? 1680556.468 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld ISO-8859-1 false 64 thrpt 20 8127116.405 ? 1128513.860 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld ISO-8859-1 true 8 thrpt 20 9405751.952 ? 2193324.806 ops/s ByteBufUtilDecodeStringBenchmark.decodeStringOld ISO-8859-1 true 64 thrpt 20 3943282.076 ? 737798.070 ops/s Benchmark result is saved to /home/norman/mainframer/netty/microbench/target/reports/performance/ByteBufUtilDecodeStringBenchmark.json Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1,030.173 sec - in io.netty.buffer.ByteBufUtilDecodeStringBenchmark [1030.460s][info ][gc,heap,exit ] Heap [1030.460s][info ][gc,heap,exit ] garbage-first heap total 516096K, used 257918K [0x0000000609a00000, 0x0000000800000000) [1030.460s][info ][gc,heap,exit ] region size 2048K, 127 young (260096K), 2 survivors (4096K) [1030.460s][info ][gc,heap,exit ] Metaspace used 17123K, capacity 17438K, committed 17792K, reserved 1064960K [1030.460s][info ][gc,heap,exit ] class space used 1709K, capacity 1827K, committed 1920K, reserved 1048576K --- .../java/io/netty/buffer/ByteBufUtil.java | 61 ++++------ .../ByteBufUtilDecodeStringBenchmark.java | 112 ++++++++++++++++++ 2 files changed, 132 insertions(+), 41 deletions(-) create mode 100644 microbench/src/main/java/io/netty/buffer/ByteBufUtilDecodeStringBenchmark.java diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index e7afab36728f..5afa5e9852f0 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -53,10 +53,10 @@ public final class ByteBufUtil { private static final InternalLogger logger = InternalLoggerFactory.getInstance(ByteBufUtil.class); - private static final FastThreadLocal CHAR_BUFFERS = new FastThreadLocal() { + private static final FastThreadLocal BYTE_ARRAYS = new FastThreadLocal() { @Override - protected CharBuffer initialValue() throws Exception { - return CharBuffer.allocate(1024); + protected byte[] initialValue() throws Exception { + return PlatformDependent.allocateUninitializedArray(1024); } }; @@ -756,52 +756,31 @@ static ByteBuf encodeString0(ByteBufAllocator alloc, boolean enforceHeap, CharBu } } + @SuppressWarnings("deprecation") static String decodeString(ByteBuf src, int readerIndex, int len, Charset charset) { if (len == 0) { return StringUtil.EMPTY_STRING; } - final CharsetDecoder decoder = CharsetUtil.decoder(charset); - final int maxLength = (int) ((double) len * decoder.maxCharsPerByte()); - CharBuffer dst = CHAR_BUFFERS.get(); - if (dst.length() < maxLength) { - dst = CharBuffer.allocate(maxLength); - if (maxLength <= MAX_CHAR_BUFFER_SIZE) { - CHAR_BUFFERS.set(dst); - } - } else { - dst.clear(); - } - if (src.nioBufferCount() == 1) { - decodeString(decoder, src.nioBuffer(readerIndex, len), dst); + final byte[] array; + final int offset; + + if (src.hasArray()) { + array = src.array(); + offset = src.arrayOffset() + readerIndex; } else { - // We use a heap buffer as CharsetDecoder is most likely able to use a fast-path if src and dst buffers - // are both backed by a byte array. - ByteBuf buffer = src.alloc().heapBuffer(len); - try { - buffer.writeBytes(src, readerIndex, len); - // Use internalNioBuffer(...) to reduce object creation. - decodeString(decoder, buffer.internalNioBuffer(buffer.readerIndex(), len), dst); - } finally { - // Release the temporary buffer again. - buffer.release(); + if (len <= 1024) { + array = BYTE_ARRAYS.get(); + } else { + array = PlatformDependent.allocateUninitializedArray(len); } + offset = 0; + src.getBytes(readerIndex, array, 0, len); } - return dst.flip().toString(); - } - - private static void decodeString(CharsetDecoder decoder, ByteBuffer src, CharBuffer dst) { - try { - CoderResult cr = decoder.decode(src, dst, true); - if (!cr.isUnderflow()) { - cr.throwException(); - } - cr = decoder.flush(dst); - if (!cr.isUnderflow()) { - cr.throwException(); - } - } catch (CharacterCodingException x) { - throw new IllegalStateException(x); + if (CharsetUtil.US_ASCII.equals(charset)) { + // Fast-path for US-ASCII which is used frequently. + return new String(array, 0, offset, len); } + return new String(array, offset, len, charset); } /** diff --git a/microbench/src/main/java/io/netty/buffer/ByteBufUtilDecodeStringBenchmark.java b/microbench/src/main/java/io/netty/buffer/ByteBufUtilDecodeStringBenchmark.java new file mode 100644 index 000000000000..367a645af094 --- /dev/null +++ b/microbench/src/main/java/io/netty/buffer/ByteBufUtilDecodeStringBenchmark.java @@ -0,0 +1,112 @@ +/* + * Copyright 2018 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: + * + * http://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.buffer; + +import io.netty.microbench.util.AbstractMicrobenchmark; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +public class ByteBufUtilDecodeStringBenchmark extends AbstractMicrobenchmark { + + public enum ByteBufType { + DIRECT { + @Override + ByteBuf newBuffer(byte[] bytes, int length) { + ByteBuf buffer = Unpooled.directBuffer(length); + buffer.writeBytes(bytes, 0, length); + return buffer; + } + }, + HEAP_OFFSET { + @Override + ByteBuf newBuffer(byte[] bytes, int length) { + return Unpooled.wrappedBuffer(bytes, 1, length); + } + }, + HEAP { + @Override + ByteBuf newBuffer(byte[] bytes, int length) { + return Unpooled.wrappedBuffer(bytes, 0, length); + } + }, + COMPOSITE { + @Override + ByteBuf newBuffer(byte[] bytes, int length) { + CompositeByteBuf buffer = Unpooled.compositeBuffer(); + int offset = 0; + // 8 buffers per composite. + int capacity = length / 8; + + while (length > 0) { + buffer.addComponent(true, Unpooled.wrappedBuffer(bytes, offset, Math.min(length, capacity))); + length -= capacity; + offset += capacity; + } + return buffer; + } + }; + + abstract ByteBuf newBuffer(byte[] bytes, int length); + } + + @Param({ "8", "64", "1024", "10240", "1073741824" }) + public int size; + + @Param({ "US-ASCII", "UTF-8" }) + public String charsetName; + + @Param + public ByteBufType bufferType; + + private ByteBuf buffer; + private Charset charset; + + @Override + protected String[] jvmArgs() { + // Ensure we minimize the GC overhead by sizing the heap big enough. + return new String[] { "-XX:MaxDirectMemorySize=2g", "-Xmx8g", "-Xms8g", "-Xmn6g" }; + } + + @Setup + public void setup() { + byte[] bytes = new byte[size + 2]; + Arrays.fill(bytes, (byte) 'a'); + + // Use an offset to not allow any optimizations because we use the exact passed in byte[] for heap buffers. + buffer = bufferType.newBuffer(bytes, size); + charset = Charset.forName(charsetName); + } + + @TearDown + public void teardown() { + buffer.release(); + } + + @Benchmark + public String decodeString() { + return ByteBufUtil.decodeString(buffer, buffer.readerIndex(), size, charset); + } +} From 91201fb338b29727b18de734f0bdbb2a07458f45 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 19 Oct 2018 17:21:04 +0200 Subject: [PATCH 222/417] Remove workaround in tests for TLSv1.3 bug in Java11 as it was fixed in 11.0.1 (#8409) Motivation: We had put some workaround in our tests due a bug in the Java11 implementation of TLSv1.3. This was now fixes as part of 11.0.1. See https://bugs.openjdk.java.net/browse/JDK-8211067. Modifications: Remove workaround in SSL tests. Result: Run all tests with supported TLS version. --- .../netty/handler/ssl/OpenSslTestUtils.java | 19 ----------- .../io/netty/handler/ssl/SSLEngineTest.java | 32 +++++++------------ .../io/netty/handler/ssl/SslErrorTest.java | 8 ++--- 3 files changed, 16 insertions(+), 43 deletions(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java index 0a3ff97f3245..e8c46ed7a056 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java @@ -15,12 +15,6 @@ */ package io.netty.handler.ssl; -import io.netty.util.internal.PlatformDependent; - -import java.util.Arrays; -import java.util.Collections; - -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; import static org.junit.Assume.assumeTrue; final class OpenSslTestUtils { @@ -34,17 +28,4 @@ static void checkShouldUseKeyManagerFactory() { static boolean isBoringSSL() { return "BoringSSL".equals(OpenSsl.versionString()); } - - static SslContextBuilder configureProtocolForMutualAuth( - SslContextBuilder ctx, SslProvider sslClientProvider, SslProvider sslServerProvider) { - if (PlatformDependent.javaVersion() >= 11 - && sslClientProvider == SslProvider.JDK && sslServerProvider != SslProvider.JDK) { - // Make sure we do not use TLSv1.3 as there seems to be a bug currently in the JDK TLSv1.3 implementation. - // See: - // - http://mail.openjdk.java.net/pipermail/security-dev/2018-September/018191.html - // - https://bugs.openjdk.java.net/projects/JDK/issues/JDK-8210846 - ctx.protocols(PROTOCOL_TLS_V1_2).ciphers(Collections.singleton("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")); - } - return ctx; - } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index fd8c9b8fc3c1..fc6826c0ee89 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -36,7 +36,6 @@ import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.handler.ssl.util.SimpleTrustManagerFactory; import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; import io.netty.util.ReferenceCountUtil; @@ -46,7 +45,6 @@ import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import org.junit.After; -import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -61,9 +59,7 @@ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; -import java.security.InvalidAlgorithmParameterException; import java.security.KeyStore; -import java.security.KeyStoreException; import java.security.Provider; import java.security.cert.Certificate; import java.security.cert.CertificateException; @@ -655,16 +651,12 @@ protected boolean mySetupMutualAuthServerIsValidException(Throwable cause) { protected void mySetupMutualAuthServerInitSslHandler(SslHandler handler) { } - private SslContextBuilder configureProtocolForMutualAuth(SslContextBuilder ctx) { - return OpenSslTestUtils.configureProtocolForMutualAuth(ctx, sslClientProvider(), sslServerProvider()); - } - private void mySetupMutualAuth(KeyManagerFactory serverKMF, final File serverTrustManager, KeyManagerFactory clientKMF, File clientTrustManager, ClientAuth clientAuth, final boolean failureExpected, final boolean serverInitEngine) throws SSLException, InterruptedException { - serverSslCtx = configureProtocolForMutualAuth( + serverSslCtx = SslContextBuilder.forServer(serverKMF) .protocols(protocols()) .ciphers(ciphers()) @@ -674,9 +666,9 @@ private void mySetupMutualAuth(KeyManagerFactory serverKMF, final File serverTru .clientAuth(clientAuth) .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) .sessionCacheSize(0) - .sessionTimeout(0)).build(); + .sessionTimeout(0).build(); - clientSslCtx = configureProtocolForMutualAuth( + clientSslCtx = SslContextBuilder.forClient() .protocols(protocols()) .ciphers(ciphers()) @@ -686,7 +678,7 @@ private void mySetupMutualAuth(KeyManagerFactory serverKMF, final File serverTru .keyManager(clientKMF) .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) .sessionCacheSize(0) - .sessionTimeout(0)).build(); + .sessionTimeout(0).build(); serverConnectedChannel = null; sb = new ServerBootstrap(); @@ -941,7 +933,7 @@ private void mySetupMutualAuth( File servertTrustCrtFile, File serverKeyFile, final File serverCrtFile, String serverKeyPassword, File clientTrustCrtFile, File clientKeyFile, File clientCrtFile, String clientKeyPassword) throws InterruptedException, SSLException { - serverSslCtx = configureProtocolForMutualAuth( + serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile, serverKeyPassword) .sslProvider(sslServerProvider()) .sslContextProvider(serverSslContextProvider()) @@ -950,8 +942,8 @@ private void mySetupMutualAuth( .trustManager(servertTrustCrtFile) .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) .sessionCacheSize(0) - .sessionTimeout(0)).build(); - clientSslCtx = configureProtocolForMutualAuth( + .sessionTimeout(0).build(); + clientSslCtx = SslContextBuilder.forClient() .sslProvider(sslClientProvider()) .sslContextProvider(clientSslContextProvider()) @@ -961,7 +953,7 @@ private void mySetupMutualAuth( .keyManager(clientCrtFile, clientKeyFile, clientKeyPassword) .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) .sessionCacheSize(0) - .sessionTimeout(0)).build(); + .sessionTimeout(0).build(); serverConnectedChannel = null; sb = new ServerBootstrap(); @@ -1611,7 +1603,7 @@ public void channelInactive(ChannelHandlerContext ctx) throws Exception { @Test(timeout = 30000) public void testMutualAuthSameCertChain() throws Exception { - serverSslCtx = configureProtocolForMutualAuth( + serverSslCtx = SslContextBuilder.forServer( new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8)), new ByteArrayInputStream(PRIVATE_KEY_PEM.getBytes(CharsetUtil.UTF_8))) @@ -1619,7 +1611,7 @@ public void testMutualAuthSameCertChain() throws Exception { .clientAuth(ClientAuth.REQUIRE).sslProvider(sslServerProvider()) .sslContextProvider(serverSslContextProvider()) .protocols(protocols()) - .ciphers(ciphers())).build(); + .ciphers(ciphers()).build(); sb = new ServerBootstrap(); sb.group(new NioEventLoopGroup(), new NioEventLoopGroup()); @@ -1670,14 +1662,14 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc } }).bind(new InetSocketAddress(0)).syncUninterruptibly().channel(); - clientSslCtx = configureProtocolForMutualAuth( + clientSslCtx = SslContextBuilder.forClient().keyManager( new ByteArrayInputStream(CLIENT_X509_CERT_CHAIN_PEM.getBytes(CharsetUtil.UTF_8)), new ByteArrayInputStream(CLIENT_PRIVATE_KEY_PEM.getBytes(CharsetUtil.UTF_8))) .trustManager(new ByteArrayInputStream(X509_CERT_PEM.getBytes(CharsetUtil.UTF_8))) .sslProvider(sslClientProvider()) .sslContextProvider(clientSslContextProvider()) - .protocols(protocols()).ciphers(ciphers())).build(); + .protocols(protocols()).ciphers(ciphers()).build(); cb = new Bootstrap(); cb.group(new NioEventLoopGroup()); cb.channel(NioSocketChannel.class); diff --git a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java index ab7de93787c6..97727ac1fdef 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java @@ -124,7 +124,7 @@ public void testCorrectAlert() throws Exception { Assume.assumeTrue(OpenSsl.isAvailable()); SelfSignedCertificate ssc = new SelfSignedCertificate(); - final SslContext sslServerCtx = OpenSslTestUtils.configureProtocolForMutualAuth( + final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .sslProvider(serverProvider) .trustManager(new SimpleTrustManagerFactory() { @@ -155,13 +155,13 @@ public X509Certificate[] getAcceptedIssuers() { } } }; } - }).clientAuth(ClientAuth.REQUIRE), clientProvider, serverProvider).build(); + }).clientAuth(ClientAuth.REQUIRE).build(); - final SslContext sslClientCtx = OpenSslTestUtils.configureProtocolForMutualAuth(SslContextBuilder.forClient() + final SslContext sslClientCtx = SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .keyManager(new File(getClass().getResource("test.crt").getFile()), new File(getClass().getResource("test_unencrypted.pem").getFile())) - .sslProvider(clientProvider), clientProvider, serverProvider).build(); + .sslProvider(clientProvider).build(); Channel serverChannel = null; Channel clientChannel = null; From a93ff3a0e4b909166dc42ecb236a13a439f47eac Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 20 Oct 2018 22:03:27 +0200 Subject: [PATCH 223/417] Upgrade commons-compress to 2.0.18 (#8416) Motivation: Commons-compress < 2.0.18 has a security flaw so we should upgrade (even if we only use it in tests anyway). Modifications: Update to 2.0.18 Result: Use latest version. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0e66e2461f0c..162696d26193 100644 --- a/pom.xml +++ b/pom.xml @@ -559,7 +559,7 @@ org.apache.commons commons-compress - 1.12 + 1.18 test From fc35e20e2caad93b7f9e06f39a6a201a1857b5c7 Mon Sep 17 00:00:00 2001 From: almson Date: Mon, 22 Oct 2018 09:01:38 -0400 Subject: [PATCH 224/417] Include correct duped value in DefaultResourceLeak.toString() (#8413) Motivation: DefaultResourceLeak.toString() did include the wrong value for duplicated records. Modifications: Include the correct value. Result: Correct toString() implementation. --- common/src/main/java/io/netty/util/ResourceLeakDetector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetector.java b/common/src/main/java/io/netty/util/ResourceLeakDetector.java index c29320adb7d3..da022b01b7ce 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetector.java +++ b/common/src/main/java/io/netty/util/ResourceLeakDetector.java @@ -501,7 +501,7 @@ public String toString() { if (duped > 0) { buf.append(": ") - .append(dropped) + .append(duped) .append(" leak records were discarded because they were duplicates") .append(NEWLINE); } From 9e762e8816fef48e3cfba563c55f8c9cd6bce010 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 23 Oct 2018 17:08:23 +0200 Subject: [PATCH 225/417] Correctly detect if KeyManagerFactory is supported by OpenSSL even when sun.security.x509.* can not be accessed and bouncycastle is not on the classpath. (#8415) Motivation: OpenSsl used SelfSignedCertificate in its static init block to detect if KeyManagerFactory is supported. Unfortunally this only works when either sun.security.x509.* can be accessed or bouncycastle is on the classpath. We should not depend on either of it. This came up in https://github.com/netty/netty-tcnative/issues/404#issuecomment-431551890. Modifications: Just directly use the bytes to generate the X509Certificate and so not depend on sun.security.x509.* / bouncycastle. Result: Correctly be able to detect if KeyManagerFactory can be supported in all cases. --- .../java/io/netty/handler/ssl/OpenSsl.java | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index 073c855a4a6d..c5086cc14d98 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -18,7 +18,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; -import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.internal.tcnative.Buffer; import io.netty.internal.tcnative.Library; import io.netty.internal.tcnative.SSL; @@ -31,8 +30,11 @@ import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; +import java.io.ByteArrayInputStream; import java.security.AccessController; import java.security.PrivilegedAction; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; @@ -142,7 +144,6 @@ public final class OpenSsl { try { final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER); long certBio = 0; - SelfSignedCertificate cert = null; try { try { SSLContext.setCipherSuite(sslCtx, TLSV13_CIPHERS, true); @@ -172,8 +173,40 @@ public final class OpenSsl { logger.debug("Hostname Verification not supported."); } try { - cert = new SelfSignedCertificate(); - certBio = ReferenceCountedOpenSslContext.toBIO(ByteBufAllocator.DEFAULT, cert.cert()); + // Bytes of self-signed certificate for netty.io + byte[] certBytes = { + 48, -126, 1, -92, 48, -126, 1, 13, -96, 3, 2, 1, 2, 2, 9, 0, -9, 61, + 44, 121, -118, -4, -45, -120, 48, 13, 6, 9, 42, -122, 72, -122, + -9, 13, 1, 1, 5, 5, 0, 48, 19, 49, 17, 48, 15, 6, 3, 85, 4, 3, 19, + 8, 110, 101, 116, 116, 121, 46, 105, 111, 48, 32, 23, 13, 49, 55, + 49, 48, 50, 48, 49, 56, 49, 54, 51, 54, 90, 24, 15, 57, 57, 57, 57, + 49, 50, 51, 49, 50, 51, 53, 57, 53, 57, 90, 48, 19, 49, 17, 48, 15, + 6, 3, 85, 4, 3, 19, 8, 110, 101, 116, 116, 121, 46, 105, 111, 48, -127, + -97, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -127, + -115, 0, 48, -127, -119, 2, -127, -127, 0, -116, 37, 122, -53, 28, 46, + 13, -90, -14, -33, 111, -108, -41, 59, 90, 124, 113, -112, -66, -17, + -102, 44, 13, 7, -33, -28, 24, -79, -126, -76, 40, 111, -126, -103, + -102, 34, 11, 45, 16, -38, 63, 24, 80, 24, 76, 88, -93, 96, 11, 38, + -19, -64, -11, 87, -49, -52, -65, 24, 36, -22, 53, 8, -42, 14, -121, + 114, 6, 17, -82, 10, 92, -91, -127, 81, -12, -75, 105, -10, -106, 91, + -38, 111, 50, 57, -97, -125, 109, 42, -87, -1, -19, 80, 78, 49, -97, -4, + 23, -2, -103, 122, -107, -43, 4, -31, -21, 90, 39, -9, -106, 34, -101, + -116, 31, -94, -84, 80, -6, -78, -33, 87, -90, 31, 103, 100, 56, -103, + -5, 11, 2, 3, 1, 0, 1, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, + 5, 5, 0, 3, -127, -127, 0, 112, 45, -73, 5, 64, 49, 59, 101, 51, 73, + -96, 62, 23, -84, 90, -41, -58, 83, -20, -72, 38, 123, -108, -45, 28, + 96, -122, -18, 30, 42, 86, 87, -87, -28, 107, 110, 11, -59, 91, 100, + 101, -18, 26, -103, -78, -80, -3, 38, 113, 83, -48, -108, 109, 41, -15, + 6, 112, 105, 7, -46, -11, -3, -51, 40, -66, -73, -83, -46, -94, -121, + -88, 51, -106, -77, 109, 53, -7, 123, 91, 75, -105, -22, 64, 121, -72, + -59, -21, -44, 84, 12, 9, 120, 21, -26, 13, 49, -81, -58, -47, 117, + -44, -18, -17, 124, 49, -48, 19, 16, -41, 71, -52, -107, 99, -19, -29, + 105, -93, -71, -38, -97, -128, -2, 118, 119, 49, -126, 109, 119 }; + + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate certificate = (X509Certificate) cf.generateCertificate( + new ByteArrayInputStream(certBytes)); + certBio = ReferenceCountedOpenSslContext.toBIO(ByteBufAllocator.DEFAULT, certificate); SSL.setCertificateChainBio(ssl, certBio, false); supportsKeyManagerFactory = true; try { @@ -195,9 +228,6 @@ public Boolean run() { if (certBio != 0) { SSL.freeBIO(certBio); } - if (cert != null) { - cert.delete(); - } } } finally { SSLContext.free(sslCtx); From 0cdd9de6decbb7c07c83640c367b4f34473970ff Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 23 Oct 2018 14:55:19 -0700 Subject: [PATCH 226/417] Update to JDK 12 EA16 when running CI jobs against JDK 12. (#8421) Motivation: A new EA release was done, we should always run against the latest. Modifications: Update to EA 16. Result: CI runs with latest EA release for JDK12. --- docker/docker-compose.centos-6.112.yaml | 2 +- docker/docker-compose.centos-7.112.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.112.yaml b/docker/docker-compose.centos-6.112.yaml index 456d603e8f23..c105f5abde00 100644 --- a/docker/docker-compose.centos-6.112.yaml +++ b/docker/docker-compose.centos-6.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "openjdk@1.12.0-15" + java_version : "openjdk@1.12.0-16" test: image: netty:centos-6-1.12 diff --git a/docker/docker-compose.centos-7.112.yaml b/docker/docker-compose.centos-7.112.yaml index 73bae6fdec40..4df31de71261 100644 --- a/docker/docker-compose.centos-7.112.yaml +++ b/docker/docker-compose.centos-7.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "openjdk@1.12.0-15" + java_version : "openjdk@1.12.0-16" test: image: netty:centos-7-1.12 From 1cc692dd7de54bfffa05813a90b14670b4eb50d1 Mon Sep 17 00:00:00 2001 From: almson Date: Wed, 24 Oct 2018 16:15:13 -0400 Subject: [PATCH 227/417] Fix incorrect reachability assumption in ResourceLeakDetector (#8410) Motivation: trackedObject != null gives no guarantee that trackedObject remains reachable. This may cause problems related to premature finalization: false leak detector warnings. Modifications: Add private method reachabilityFence0 that works on JDK 8 and can be factored out into PlatformDependent. Later, it can be swapped for the real Reference.reachabilityFence. Result: No false leak detector warnings in future versions of JDK. --- .../io/netty/util/ResourceLeakDetector.java | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetector.java b/common/src/main/java/io/netty/util/ResourceLeakDetector.java index da022b01b7ce..5f26926cf7a0 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetector.java +++ b/common/src/main/java/io/netty/util/ResourceLeakDetector.java @@ -461,11 +461,41 @@ public boolean close(T trackedObject) { // Ensure that the object that was tracked is the same as the one that was passed to close(...). assert trackedHash == System.identityHashCode(trackedObject); - // We need to actually do the null check of the trackedObject after we close the leak because otherwise - // we may get false-positives reported by the ResourceLeakDetector. This can happen as the JIT / GC may - // be able to figure out that we do not need the trackedObject anymore and so already enqueue it for - // collection before we actually get a chance to close the enclosing ResourceLeak. - return close() && trackedObject != null; + try { + return close(); + } finally { + // This method will do `synchronized(trackedObject)` and we should be sure this will not cause deadlock. + // It should not, because somewhere up the callstack should be a (successful) `trackedObject.release`, + // therefore it is unreasonable that anyone else, anywhere, is holding a lock on the trackedObject. + // (Unreasonable but possible, unfortunately.) + reachabilityFence0(trackedObject); + } + } + + /** + * Ensures that the object referenced by the given reference remains + * strongly reachable, + * regardless of any prior actions of the program that might otherwise cause + * the object to become unreachable; thus, the referenced object is not + * reclaimable by garbage collection at least until after the invocation of + * this method. + * + *

Recent versions of the JDK have a nasty habit of prematurely deciding objects are unreachable. + * see: https://stackoverflow.com/questions/26642153/finalize-called-on-strongly-reachable-object-in-java-8 + * The Java 9 method Reference.reachabilityFence offers a solution to this problem. + * + *

This method is always implemented as a synchronization on {@code ref}, not as + * {@code Reference.reachabilityFence} for consistency across platforms and to allow building on JDK 6-8. + * It is the caller's responsibility to ensure that this synchronization will not cause deadlock. + * + * @param ref the reference. If {@code null}, this method has no effect. + * @see java.lang.ref.Reference#reachabilityFence + */ + private static void reachabilityFence0(Object ref) { + if (ref != null) { + // Empty synchronized is ok: https://stackoverflow.com/a/31933260/1151521 + synchronized (ref) { } + } } @Override From ce39773e04d6adb063cef62c0c31675272566880 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 26 Oct 2018 15:29:49 -0700 Subject: [PATCH 228/417] Add support for boringssl and TLSv1.3 (#8412) Motivation: 0ddc62cec0b4715ae37cef0e6a9f8c79d42d74e9 added support for TLSv1.3 when using openssl 1.1.1. Now that BoringSSL chromium-stable branch supports it as well we can also support it with netty-tcnative-boringssl-static. During this some unit tests failed with BoringSSL which was caused by not correctly handling flush() while the handshake is still in progress. Modification: - Upgrade netty-tcnative version which also supports TLSv1.3 when using BoringSSL - Correctly handle flush() when done while the handshake is still in progress in all cases. Result: Easier for people to enable TLSv1.3 when using native SSL impl. Ensure flush() while handshake is in progress will always be honored. --- .../java/io/netty/handler/ssl/OpenSsl.java | 22 +++++++++++++++---- .../java/io/netty/handler/ssl/SslHandler.java | 17 ++++++++------ .../netty/handler/ssl/OpenSslTestUtils.java | 4 ---- .../io/netty/handler/ssl/SSLEngineTest.java | 2 +- .../handler/ssl/SniClientJava8TestUtil.java | 2 +- .../io/netty/handler/ssl/SslErrorTest.java | 4 ++-- pom.xml | 2 +- 7 files changed, 33 insertions(+), 20 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index c5086cc14d98..e22457152c16 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -66,7 +66,7 @@ public final class OpenSsl { "TLS_AES_128_CCM_8_SHA256" + ':' + "TLS_AES_128_CCM_SHA256"; private static final boolean TLSV13_SUPPORTED; - + private static final boolean IS_BORINGSSL; static final Set SUPPORTED_PROTOCOLS_SET; static { @@ -141,6 +141,8 @@ public final class OpenSsl { boolean supportsHostNameValidation = false; boolean tlsv13Supported = false; + IS_BORINGSSL = "BoringSSL".equals(versionString()); + try { final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER); long certBio = 0; @@ -160,12 +162,19 @@ public final class OpenSsl { // Filter out bad input. if (c == null || c.isEmpty() || availableOpenSslCipherSuites.contains(c) || // Filter out TLSv1.3 ciphers if not supported. - !tlsv13Supported && SslUtils.isTLSv13Cipher(c)) { + !tlsv13Supported && isTLSv13Cipher(c)) { continue; } availableOpenSslCipherSuites.add(c); } - + if (IS_BORINGSSL) { + // Currently BoringSSL does not include these when calling SSL.getCiphers() even when these + // are supported. + Collections.addAll(availableOpenSslCipherSuites, + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384" , + "TLS_CHACHA20_POLY1305_SHA256"); + } try { SSL.setHostNameValidation(ssl, 0, "netty.io"); supportsHostNameValidation = true; @@ -240,7 +249,7 @@ public Boolean run() { AVAILABLE_OPENSSL_CIPHER_SUITES.size() * 2); for (String cipher: AVAILABLE_OPENSSL_CIPHER_SUITES) { // Included converted but also openssl cipher name - if (!SslUtils.isTLSv13Cipher(cipher)) { + if (!isTLSv13Cipher(cipher)) { availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "TLS")); availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "SSL")); } else { @@ -312,6 +321,7 @@ public Boolean run() { SUPPORTED_PROTOCOLS_SET = Collections.emptySet(); SUPPORTS_OCSP = false; TLSV13_SUPPORTED = false; + IS_BORINGSSL = false; } } @@ -510,4 +520,8 @@ static void releaseIfNeeded(ReferenceCounted counted) { static boolean isTlsv13Supported() { return TLSV13_SUPPORTED; } + + static boolean isBoringSSL() { + return IS_BORINGSSL; + } } diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index 7358d4230776..f1de1360bfea 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -1385,13 +1385,7 @@ private int unwrap( wrapLater = true; continue; } - if (flushedBeforeHandshake) { - // We need to call wrap(...) in case there was a flush done before the handshake completed. - // - // See https://github.com/netty/netty/pull/2437 - flushedBeforeHandshake = false; - wrapLater = true; - } + // If we are not handshaking and there is no more data to unwrap then the next call to unwrap // will not produce any data. We can avoid the potentially costly unwrap operation and break // out of the loop. @@ -1414,6 +1408,15 @@ private int unwrap( } } + if (flushedBeforeHandshake && handshakePromise.isDone()) { + // We need to call wrap(...) in case there was a flush done before the handshake completed to ensure + // we do not stale. + // + // See https://github.com/netty/netty/pull/2437 + flushedBeforeHandshake = false; + wrapLater = true; + } + if (wrapLater) { wrap(ctx, true); } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java index e8c46ed7a056..7882a61b4f4b 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java @@ -24,8 +24,4 @@ private OpenSslTestUtils() { static void checkShouldUseKeyManagerFactory() { assumeTrue(OpenSsl.supportsKeyManagerFactory() && OpenSsl.useKeyManagerFactory()); } - - static boolean isBoringSSL() { - return "BoringSSL".equals(OpenSsl.versionString()); - } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index fc6826c0ee89..b15a1beb111a 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -1087,7 +1087,7 @@ private static void writeAndVerifyReceived(ByteBuf message, Channel sendChannel, MessageReceiver receiver) throws Exception { List dataCapture = null; try { - sendChannel.writeAndFlush(message); + assertTrue(sendChannel.writeAndFlush(message).await(5, TimeUnit.SECONDS)); receiverLatch.await(5, TimeUnit.SECONDS); message.resetReaderIndex(); ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java index d033ca936590..26904c01179b 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java @@ -182,7 +182,7 @@ private static void assertSSLSession(boolean clientSide, SSLSession session, SNI if (clientSide) { Assert.assertEquals(0, extendedSSLSession.getPeerSupportedSignatureAlgorithms().length); } else { - if (session instanceof OpenSslSession && OpenSslTestUtils.isBoringSSL()) { + if (session instanceof OpenSslSession && OpenSsl.isBoringSSL()) { // BoringSSL does not support SSL_get_sigalgs(...) // https://boringssl.googlesource.com/boringssl/+/ba16a1e405c617f4179bd780ad15522fb25b0a65%5E%21/ Assert.assertEquals(0, extendedSSLSession.getPeerSupportedSignatureAlgorithms().length); diff --git a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java index 97727ac1fdef..9f205825e55b 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java @@ -205,7 +205,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { verifyException(unwrappedCause, "expired", promise); } else if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) { // BoringSSL uses "expired" in this case while others use "bad" - if (OpenSslTestUtils.isBoringSSL()) { + if (OpenSsl.isBoringSSL()) { verifyException(unwrappedCause, "expired", promise); } else { verifyException(unwrappedCause, "bad", promise); @@ -217,7 +217,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { verifyException(unwrappedCause, "expired", promise); } else if (exception instanceof CertificateNotYetValidException) { // BoringSSL uses "expired" in this case while others use "bad" - if (OpenSslTestUtils.isBoringSSL()) { + if (OpenSsl.isBoringSSL()) { verifyException(unwrappedCause, "expired", promise); } else { verifyException(unwrappedCause, "bad", promise); diff --git a/pom.xml b/pom.xml index 162696d26193..78187ab3f8a0 100644 --- a/pom.xml +++ b/pom.xml @@ -241,7 +241,7 @@ fedora netty-tcnative - 2.0.18.Final + 2.0.19.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber From 583d838f7c02c864c4ab4bc095c01e9b45963fcf Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Fri, 26 Oct 2018 15:32:38 -0700 Subject: [PATCH 229/417] Optimize AbstractByteBuf.getCharSequence() in US_ASCII case (#8392) * Optimize AbstractByteBuf.getCharSequence() in US_ASCII case Motivation: Inspired by https://github.com/netty/netty/pull/8388, I noticed this simple optimization to avoid char[] allocation (also suggested in a TODO here). Modifications: Return an AsciiString from AbstractByteBuf.getCharSequence() if requested charset is US_ASCII or ISO_8859_1 (latter thanks to @Scottmitch's suggestion). Also tweak unit tests not to require Strings and include a new benchmark to demonstrate the speedup. Result: Speed-up of AbstractByteBuf.getCharSequence() in ascii and iso 8859/1 cases --- .../java/io/netty/buffer/AbstractByteBuf.java | 6 +- .../io/netty/buffer/AbstractByteBufTest.java | 26 +++- .../codec/socks/SocksCmdRequestTest.java | 6 +- .../codec/socks/SocksCmdResponseTest.java | 6 +- .../handler/codec/compression/SnappyTest.java | 5 +- ...stractByteBufGetCharSequenceBenchmark.java | 124 ++++++++++++++++++ 6 files changed, 161 insertions(+), 12 deletions(-) create mode 100644 microbench/src/main/java/io/netty/buffer/AbstractByteBufGetCharSequenceBenchmark.java diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index 1fe1dbe27209..16b313a8ed94 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -15,6 +15,7 @@ */ package io.netty.buffer; +import io.netty.util.AsciiString; import io.netty.util.ByteProcessor; import io.netty.util.CharsetUtil; import io.netty.util.IllegalReferenceCountException; @@ -503,7 +504,10 @@ public ByteBuf getBytes(int index, ByteBuf dst, int length) { @Override public CharSequence getCharSequence(int index, int length, Charset charset) { - // TODO: We could optimize this for UTF8 and US_ASCII + if (CharsetUtil.US_ASCII.equals(charset) || CharsetUtil.ISO_8859_1.equals(charset)) { + // ByteBufUtil.getBytes(...) will return a new copy which the AsciiString uses directly + return new AsciiString(ByteBufUtil.getBytes(this, index, length, true), false); + } return toString(index, length, charset); } diff --git a/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java index 1af64a7392d6..59194ab374d4 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java @@ -30,6 +30,7 @@ import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.CharBuffer; import java.nio.ReadOnlyBufferException; import java.nio.channels.Channels; import java.nio.channels.FileChannel; @@ -3648,11 +3649,23 @@ public void testSetUtf16CharSequence() { testSetGetCharSequence(CharsetUtil.UTF_16); } + private static final CharBuffer EXTENDED_ASCII_CHARS, ASCII_CHARS; + + static { + char[] chars = new char[256]; + for (char c = 0; c < chars.length; c++) { + chars[c] = c; + } + EXTENDED_ASCII_CHARS = CharBuffer.wrap(chars); + ASCII_CHARS = CharBuffer.wrap(chars, 0, 128); + } + private void testSetGetCharSequence(Charset charset) { - ByteBuf buf = newBuffer(16); - String sequence = "AB"; + ByteBuf buf = newBuffer(1024); + CharBuffer sequence = CharsetUtil.US_ASCII.equals(charset) + ? ASCII_CHARS : EXTENDED_ASCII_CHARS; int bytes = buf.setCharSequence(1, sequence, charset); - assertEquals(sequence, buf.getCharSequence(1, bytes, charset)); + assertEquals(sequence, CharBuffer.wrap(buf.getCharSequence(1, bytes, charset))); buf.release(); } @@ -3677,12 +3690,13 @@ public void testWriteReadUtf16CharSequence() { } private void testWriteReadCharSequence(Charset charset) { - ByteBuf buf = newBuffer(16); - String sequence = "AB"; + ByteBuf buf = newBuffer(1024); + CharBuffer sequence = CharsetUtil.US_ASCII.equals(charset) + ? ASCII_CHARS : EXTENDED_ASCII_CHARS; buf.writerIndex(1); int bytes = buf.writeCharSequence(sequence, charset); buf.readerIndex(1); - assertEquals(sequence, buf.readCharSequence(bytes, charset)); + assertEquals(sequence, CharBuffer.wrap(buf.readCharSequence(bytes, charset))); buf.release(); } diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestTest.java index 37439a7e6af2..4013c8b929a5 100644 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestTest.java +++ b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestTest.java @@ -21,6 +21,7 @@ import org.junit.Test; import java.net.IDN; +import java.nio.CharBuffer; import static org.junit.Assert.*; @@ -101,7 +102,7 @@ public void testHostNotEncodedForUnknown() { @Test public void testIDNEncodeToAsciiForDomain() { String host = "тест.рф"; - String asciiHost = IDN.toASCII(host); + CharBuffer asciiHost = CharBuffer.wrap(IDN.toASCII(host)); short port = 10000; SocksCmdRequest rq = new SocksCmdRequest(SocksCmdType.BIND, SocksAddressType.DOMAIN, host, port); @@ -116,7 +117,8 @@ public void testIDNEncodeToAsciiForDomain() { assertEquals((byte) 0x00, buffer.readByte()); assertEquals(SocksAddressType.DOMAIN.byteValue(), buffer.readByte()); assertEquals((byte) asciiHost.length(), buffer.readUnsignedByte()); - assertEquals(asciiHost, buffer.readCharSequence(asciiHost.length(), CharsetUtil.US_ASCII)); + assertEquals(asciiHost, + CharBuffer.wrap(buffer.readCharSequence(asciiHost.length(), CharsetUtil.US_ASCII))); assertEquals(port, buffer.readUnsignedShort()); buffer.release(); diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseTest.java index 6861f761de5b..3ca64252d5c5 100644 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseTest.java +++ b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseTest.java @@ -21,6 +21,7 @@ import org.junit.Test; import java.net.IDN; +import java.nio.CharBuffer; import static org.junit.Assert.*; @@ -135,7 +136,7 @@ public void testHostNotEncodedForUnknown() { @Test public void testIDNEncodeToAsciiForDomain() { String host = "тест.рф"; - String asciiHost = IDN.toASCII(host); + CharBuffer asciiHost = CharBuffer.wrap(IDN.toASCII(host)); short port = 10000; SocksCmdResponse rs = new SocksCmdResponse(SocksCmdStatus.SUCCESS, SocksAddressType.DOMAIN, host, port); @@ -150,7 +151,8 @@ public void testIDNEncodeToAsciiForDomain() { assertEquals((byte) 0x00, buffer.readByte()); assertEquals(SocksAddressType.DOMAIN.byteValue(), buffer.readByte()); assertEquals((byte) asciiHost.length(), buffer.readUnsignedByte()); - assertEquals(asciiHost, buffer.readCharSequence(asciiHost.length(), CharsetUtil.US_ASCII)); + assertEquals(asciiHost, + CharBuffer.wrap(buffer.readCharSequence(asciiHost.length(), CharsetUtil.US_ASCII))); assertEquals(port, buffer.readUnsignedShort()); buffer.release(); diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java index 232804709392..115deef15804 100644 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java +++ b/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java @@ -24,6 +24,8 @@ import static io.netty.handler.codec.compression.Snappy.*; import static org.junit.Assert.*; +import java.nio.CharBuffer; + public class SnappyTest { private final Snappy snappy = new Snappy(); @@ -219,7 +221,8 @@ public void encodeAndDecodeLongTextUsesCopy() throws Exception { // Decode ByteBuf outDecoded = Unpooled.buffer(); snappy.decode(out, outDecoded); - assertEquals(srcStr, outDecoded.getCharSequence(0, outDecoded.writerIndex(), CharsetUtil.US_ASCII)); + assertEquals(CharBuffer.wrap(srcStr), + CharBuffer.wrap(outDecoded.getCharSequence(0, outDecoded.writerIndex(), CharsetUtil.US_ASCII))); in.release(); out.release(); diff --git a/microbench/src/main/java/io/netty/buffer/AbstractByteBufGetCharSequenceBenchmark.java b/microbench/src/main/java/io/netty/buffer/AbstractByteBufGetCharSequenceBenchmark.java new file mode 100644 index 000000000000..6fcdb94cc893 --- /dev/null +++ b/microbench/src/main/java/io/netty/buffer/AbstractByteBufGetCharSequenceBenchmark.java @@ -0,0 +1,124 @@ +/* + * Copyright 2018 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: + * + * http://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.buffer; + +import io.netty.microbench.util.AbstractMicrobenchmark; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +public class AbstractByteBufGetCharSequenceBenchmark extends AbstractMicrobenchmark { + + public enum ByteBufType { + DIRECT { + @Override + ByteBuf newBuffer(byte[] bytes, int length) { + ByteBuf buffer = Unpooled.directBuffer(length); + buffer.writeBytes(bytes, 0, length); + return buffer; + } + }, + HEAP_OFFSET { + @Override + ByteBuf newBuffer(byte[] bytes, int length) { + return Unpooled.wrappedBuffer(bytes, 1, length); + } + }, + HEAP { + @Override + ByteBuf newBuffer(byte[] bytes, int length) { + return Unpooled.wrappedBuffer(bytes, 0, length); + } + }, + COMPOSITE { + @Override + ByteBuf newBuffer(byte[] bytes, int length) { + CompositeByteBuf buffer = Unpooled.compositeBuffer(); + int offset = 0; + // 8 buffers per composite. + int capacity = length / 8; + + while (length > 0) { + buffer.addComponent(true, Unpooled.wrappedBuffer(bytes, offset, Math.min(length, capacity))); + length -= capacity; + offset += capacity; + } + return buffer; + } + }; + abstract ByteBuf newBuffer(byte[] bytes, int length); + } + + @Param({ "8", "64", "1024", "10240", "1073741824" }) + public int size; + + @Param({ "US-ASCII", "ISO_8859_1" }) + public String charsetName; + + @Param + public ByteBufType bufferType; + + private ByteBuf buffer; + private Charset charset; + + @Override + protected String[] jvmArgs() { + // Ensure we minimize the GC overhead by sizing the heap big enough. + return new String[] { "-XX:MaxDirectMemorySize=2g", "-Xmx8g", "-Xms8g", "-Xmn6g" }; + } + + @Setup + public void setup() { + byte[] bytes = new byte[size + 2]; + Arrays.fill(bytes, (byte) 'a'); + + // Use an offset to not allow any optimizations because we use the exact passed in byte[] for heap buffers. + buffer = bufferType.newBuffer(bytes, size); + charset = Charset.forName(charsetName); + } + + @TearDown + public void teardown() { + buffer.release(); + } + + @Benchmark + public int getCharSequence() { + return traverse(buffer.getCharSequence(buffer.readerIndex(), size, charset)); + } + + @Benchmark + public int getCharSequenceOld() { + return traverse(buffer.toString(buffer.readerIndex(), size, charset)); + } + + private static int traverse(CharSequence cs) { + int i = 0, len = cs.length(); + while (i < len && cs.charAt(i++) != 0) { + // ensure result is "used" + } + return i; + } +} From d7fa7be67fb3cd5020ab89b64b311ff3dc7c82bb Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Sat, 27 Oct 2018 08:43:28 -0700 Subject: [PATCH 230/417] Exploit PlatformDependent.allocateUninitializedArray() in more places (#8393) Motivation: There are currently many more places where this could be used which were possibly not considered when the method was added. If https://github.com/netty/netty/pull/8388 is included in its current form, a number of these places could additionally make use of the same BYTE_ARRAYS threadlocal. There's also a couple of adjacent places where an optimistically-pooled heap buffer is used for temp byte storage which could use the threadlocal too in preference to allocating a temp heap bytebuf wrapper. For example https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java#L1417. Modifications: Replace new byte[] with PlatformDependent.allocateUninitializedArray() where appropriate; make use of ByteBufUtil.getBytes() in some places which currently perform the equivalent logic, including avoiding copy of backing array if possible (although would be rare). Result: Further potential speed-up with java9+ and appropriate compile flags. Many of these places could be on latency-sensitive code paths. --- .../main/java/io/netty/buffer/ByteBufUtil.java | 5 +++-- .../io/netty/buffer/PooledDirectByteBuf.java | 3 ++- .../io/netty/buffer/ReadOnlyByteBufferBuf.java | 3 ++- .../src/main/java/io/netty/buffer/Unpooled.java | 10 +++++----- .../io/netty/buffer/UnpooledDirectByteBuf.java | 2 +- .../io/netty/buffer/UnpooledHeapByteBuf.java | 2 +- .../java/io/netty/buffer/UnsafeByteBufUtil.java | 3 ++- .../handler/codec/bytes/ByteArrayDecoder.java | 6 ++---- .../handler/codec/protobuf/ProtobufDecoder.java | 4 ++-- .../codec/protobuf/ProtobufDecoderNano.java | 4 ++-- .../src/main/java/io/netty/util/AsciiString.java | 16 ++++++++-------- .../channel/socket/oio/OioDatagramChannel.java | 5 ++--- 12 files changed, 32 insertions(+), 31 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index 5afa5e9852f0..500e5e9c67e8 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -837,7 +837,7 @@ public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy) } } - byte[] v = new byte[length]; + byte[] v = PlatformDependent.allocateUninitializedArray(length); buf.getBytes(start, v); return v; } @@ -1403,7 +1403,8 @@ static void readBytes(ByteBufAllocator allocator, ByteBuffer buffer, int positio tmpBuf.release(); } } else { - getBytes(buffer, new byte[chunkLen], 0, chunkLen, out, length); + byte[] tmp = PlatformDependent.allocateUninitializedArray(length); + getBytes(buffer, tmp, 0, chunkLen, out, length); } } } diff --git a/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java index 3c43509c0545..8b326ccef744 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java @@ -17,6 +17,7 @@ package io.netty.buffer; import io.netty.util.Recycler; +import io.netty.util.internal.PlatformDependent; import java.io.IOException; import java.io.InputStream; @@ -351,7 +352,7 @@ public ByteBuf setBytes(int index, ByteBuffer src) { @Override public int setBytes(int index, InputStream in, int length) throws IOException { checkIndex(index, length); - byte[] tmp = new byte[length]; + byte[] tmp = PlatformDependent.allocateUninitializedArray(length); int readBytes = in.read(tmp); if (readBytes <= 0) { return readBytes; diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java index 406514f384b0..ec34930ee11b 100644 --- a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java @@ -15,6 +15,7 @@ */ package io.netty.buffer; +import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import java.io.IOException; @@ -355,7 +356,7 @@ public ByteBuf getBytes(int index, OutputStream out, int length) throws IOExcept if (buffer.hasArray()) { out.write(buffer.array(), index + buffer.arrayOffset(), length); } else { - byte[] tmp = new byte[length]; + byte[] tmp = PlatformDependent.allocateUninitializedArray(length); ByteBuffer tmpBuf = internalNioBuffer(); tmpBuf.clear().position(index); tmpBuf.get(tmp); diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/netty/buffer/Unpooled.java index f6bec336dda7..1c917b42c22e 100644 --- a/buffer/src/main/java/io/netty/buffer/Unpooled.java +++ b/buffer/src/main/java/io/netty/buffer/Unpooled.java @@ -400,7 +400,7 @@ public static ByteBuf copiedBuffer(byte[] array, int offset, int length) { if (length == 0) { return EMPTY_BUFFER; } - byte[] copy = new byte[length]; + byte[] copy = PlatformDependent.allocateUninitializedArray(length); System.arraycopy(array, offset, copy, 0, length); return wrappedBuffer(copy); } @@ -416,7 +416,7 @@ public static ByteBuf copiedBuffer(ByteBuffer buffer) { if (length == 0) { return EMPTY_BUFFER; } - byte[] copy = new byte[length]; + byte[] copy = PlatformDependent.allocateUninitializedArray(length); // Duplicate the buffer so we not adjust the position during our get operation. // See https://github.com/netty/netty/issues/3896 ByteBuffer duplicate = buffer.duplicate(); @@ -473,7 +473,7 @@ public static ByteBuf copiedBuffer(byte[]... arrays) { return EMPTY_BUFFER; } - byte[] mergedArray = new byte[length]; + byte[] mergedArray = PlatformDependent.allocateUninitializedArray(length); for (int i = 0, j = 0; i < arrays.length; i ++) { byte[] a = arrays[i]; System.arraycopy(a, 0, mergedArray, j, a.length); @@ -527,7 +527,7 @@ public static ByteBuf copiedBuffer(ByteBuf... buffers) { return EMPTY_BUFFER; } - byte[] mergedArray = new byte[length]; + byte[] mergedArray = PlatformDependent.allocateUninitializedArray(length); for (int i = 0, j = 0; i < buffers.length; i ++) { ByteBuf b = buffers[i]; int bLen = b.readableBytes(); @@ -582,7 +582,7 @@ public static ByteBuf copiedBuffer(ByteBuffer... buffers) { return EMPTY_BUFFER; } - byte[] mergedArray = new byte[length]; + byte[] mergedArray = PlatformDependent.allocateUninitializedArray(length); for (int i = 0, j = 0; i < buffers.length; i ++) { // Duplicate the buffer so we not adjust the position during our get operation. // See https://github.com/netty/netty/issues/3896 diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java index 74c0509a7603..71ab3df86a3f 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java @@ -561,7 +561,7 @@ public int setBytes(int index, InputStream in, int length) throws IOException { if (buffer.hasArray()) { return in.read(buffer.array(), buffer.arrayOffset() + index, length); } else { - byte[] tmp = new byte[length]; + byte[] tmp = PlatformDependent.allocateUninitializedArray(length); int readBytes = in.read(tmp); if (readBytes <= 0) { return readBytes; diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java index 0133845f822c..f37ceb0559aa 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java @@ -536,7 +536,7 @@ protected void _setLongLE(int index, long value) { @Override public ByteBuf copy(int index, int length) { checkIndex(index, length); - byte[] copiedArray = new byte[length]; + byte[] copiedArray = PlatformDependent.allocateUninitializedArray(length); System.arraycopy(array, index, copiedArray, 0, length); return new UnpooledHeapByteBuf(alloc(), copiedArray, maxCapacity()); } diff --git a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java index 7d866d5b433a..ead07253bdaf 100644 --- a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java @@ -595,7 +595,8 @@ static void getBytes(AbstractByteBuf buf, long addr, int index, OutputStream out tmpBuf.release(); } } else { - getBytes(addr, new byte[len], 0, len, out, length); + byte[] tmp = PlatformDependent.allocateUninitializedArray(len); + getBytes(addr, tmp, 0, len, out, length); } } } diff --git a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java b/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java index 1fe78c505125..21429d1867b1 100644 --- a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java @@ -16,6 +16,7 @@ package io.netty.handler.codec.bytes; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; @@ -52,9 +53,6 @@ public class ByteArrayDecoder extends MessageToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception { // copy the ByteBuf content to a byte array - byte[] array = new byte[msg.readableBytes()]; - msg.getBytes(0, array); - - out.add(array); + out.add(ByteBufUtil.getBytes(msg)); } } diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java index d48bfbf7a905..9ef56f11d4f4 100644 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java @@ -20,6 +20,7 @@ import com.google.protobuf.Message; import com.google.protobuf.MessageLite; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; @@ -111,8 +112,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) array = msg.array(); offset = msg.arrayOffset() + msg.readerIndex(); } else { - array = new byte[length]; - msg.getBytes(msg.readerIndex(), array, 0, length); + array = ByteBufUtil.getBytes(msg, msg.readerIndex(), length, false); offset = 0; } diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoderNano.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoderNano.java index 5144b5fa3dd8..0d6685c979bd 100644 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoderNano.java +++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoderNano.java @@ -20,6 +20,7 @@ import java.util.List; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; @@ -78,8 +79,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) array = msg.array(); offset = msg.arrayOffset() + msg.readerIndex(); } else { - array = new byte[length]; - msg.getBytes(msg.readerIndex(), array, 0, length); + array = ByteBufUtil.getBytes(msg, msg.readerIndex(), length, false); offset = 0; } MessageNano prototype = clazz.getConstructor().newInstance(); diff --git a/common/src/main/java/io/netty/util/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java index 2730923f8dd3..3d3e1fbc21d2 100644 --- a/common/src/main/java/io/netty/util/AsciiString.java +++ b/common/src/main/java/io/netty/util/AsciiString.java @@ -146,7 +146,7 @@ public AsciiString(ByteBuffer value, int start, int length, boolean copy) { this.offset = start; } } else { - this.value = new byte[length]; + this.value = PlatformDependent.allocateUninitializedArray(length); int oldPos = value.position(); value.get(this.value, 0, length); value.position(oldPos); @@ -172,7 +172,7 @@ public AsciiString(char[] value, int start, int length) { + ") <= " + "value.length(" + value.length + ')'); } - this.value = new byte[length]; + this.value = PlatformDependent.allocateUninitializedArray(length); for (int i = 0, j = start; i < length; i++, j++) { this.value[i] = c2b(value[j]); } @@ -219,7 +219,7 @@ public AsciiString(CharSequence value, int start, int length) { + ") <= " + "value.length(" + value.length() + ')'); } - this.value = new byte[length]; + this.value = PlatformDependent.allocateUninitializedArray(length); for (int i = 0, j = start; i < length; i++, j++) { this.value[i] = c2b(value.charAt(j)); } @@ -483,7 +483,7 @@ public AsciiString concat(CharSequence string) { return that; } - byte[] newValue = new byte[thisLen + thatLen]; + byte[] newValue = PlatformDependent.allocateUninitializedArray(thisLen + thatLen); System.arraycopy(value, arrayOffset(), newValue, 0, thisLen); System.arraycopy(that.value, that.arrayOffset(), newValue, thisLen, thatLen); return new AsciiString(newValue, false); @@ -493,7 +493,7 @@ public AsciiString concat(CharSequence string) { return new AsciiString(string); } - byte[] newValue = new byte[thisLen + thatLen]; + byte[] newValue = PlatformDependent.allocateUninitializedArray(thisLen + thatLen); System.arraycopy(value, arrayOffset(), newValue, 0, thisLen); for (int i = thisLen, j = 0; i < newValue.length; i++, j++) { newValue[i] = c2b(string.charAt(j)); @@ -881,7 +881,7 @@ public AsciiString replace(char oldChar, char newChar) { final int len = offset + length; for (int i = offset; i < len; ++i) { if (value[i] == oldCharAsByte) { - byte[] buffer = new byte[length()]; + byte[] buffer = PlatformDependent.allocateUninitializedArray(length()); System.arraycopy(value, offset, buffer, 0, i - offset); buffer[i - offset] = newCharAsByte; ++i; @@ -942,7 +942,7 @@ public AsciiString toLowerCase() { return this; } - final byte[] newValue = new byte[length()]; + final byte[] newValue = PlatformDependent.allocateUninitializedArray(length()); for (i = 0, j = arrayOffset(); i < newValue.length; ++i, ++j) { newValue[i] = toLowerCase(value[j]); } @@ -972,7 +972,7 @@ public AsciiString toUpperCase() { return this; } - final byte[] newValue = new byte[length()]; + final byte[] newValue = PlatformDependent.allocateUninitializedArray(length()); for (i = 0, j = arrayOffset(); i < newValue.length; ++i, ++j) { newValue[i] = toUpperCase(value[j]); } diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java index 3e588d5a369d..a283db74232c 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java @@ -16,6 +16,7 @@ package io.netty.channel.socket.oio; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.AddressedEnvelope; import io.netty.channel.Channel; import io.netty.channel.ChannelException; @@ -276,9 +277,7 @@ protected void doWrite(ChannelOutboundBuffer in) throws Exception { if (data.hasArray()) { tmpPacket.setData(data.array(), data.arrayOffset() + data.readerIndex(), length); } else { - byte[] tmp = new byte[length]; - data.getBytes(data.readerIndex(), tmp); - tmpPacket.setData(tmp); + tmpPacket.setData(ByteBufUtil.getBytes(data, data.readerIndex(), length)); } socket.send(tmpPacket); in.remove(); From b6522927d71d1c24e4e362165d5f70535046b5fe Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 28 Oct 2018 10:27:34 +0100 Subject: [PATCH 231/417] Add profile to easily run testsuite against netty-tcnative-boringssl-static (#8436) Motivation: We should provide an easy way to run our testsuite against netty-tcnative-boringssl-static Modifications: - Add boringssl profile which can be used to enable usage of netty-tcnative-boringssl-static - Make use of the profile in docker-compose Result: Cleaner and easier way of running testsuite against netty-tcnative-boringssl-static --- docker/docker-compose.yaml | 2 +- pom.xml | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 3b8d5d0e223e..f0183cd283b3 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -27,7 +27,7 @@ services: test-boringssl-static: <<: *common - command: /bin/bash -cl "./mvnw clean install -Dio.netty.testsuite.badHost=netty.io -Dxml.skip=true -Dtcnative.artifactId=netty-tcnative-boringssl-static -Dtcnative.classifier=" + command: /bin/bash -cl "./mvnw -P boringssl clean install -Dio.netty.testsuite.badHost=netty.io -Dxml.skip=true" shell: <<: *common diff --git a/pom.xml b/pom.xml index 78187ab3f8a0..c2b0e799f9a6 100644 --- a/pom.xml +++ b/pom.xml @@ -141,6 +141,13 @@ + + boringssl + + netty-tcnative-boringssl-static + + + leak From 48c45cf4aceb1e6e9dce8a0670af79570a11f369 Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Sun, 28 Oct 2018 02:28:18 -0700 Subject: [PATCH 232/417] Fix leak and corruption bugs in CompositeByteBuf (#8438) Motivation: I came across two bugs: - Components removed due to capacity reduction aren't released - Offsets aren't set correctly on empty components that are added between existing components Modifications: Add unit tests which expose these bugs, fix them. Result: Bugs are fixed --- .../io/netty/buffer/CompositeByteBuf.java | 26 ++++------ .../buffer/AbstractCompositeByteBufTest.java | 50 +++++++++++++++++++ 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index f987e48681c4..f66a4b91d51a 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -260,24 +260,18 @@ private int addComponent0(boolean increaseWriterIndex, int cIndex, ByteBuf buffe // No need to consolidate - just add a component to the list. @SuppressWarnings("deprecation") Component c = new Component(buffer.order(ByteOrder.BIG_ENDIAN).slice()); - if (cIndex == components.size()) { - wasAdded = components.add(c); - if (cIndex == 0) { - c.endOffset = readableBytes; - } else { - Component prev = components.get(cIndex - 1); - c.offset = prev.endOffset; - c.endOffset = c.offset + readableBytes; - } + components.add(cIndex, c); + wasAdded = true; + if (readableBytes > 0 && cIndex < components.size() - 1) { + updateComponentOffsets(cIndex); + } else if (cIndex == 0) { + c.endOffset = readableBytes; } else { - components.add(cIndex, c); - wasAdded = true; - if (readableBytes != 0) { - updateComponentOffsets(cIndex); - } + c.offset = components.get(cIndex - 1).endOffset; + c.endOffset = c.offset + readableBytes; } if (increaseWriterIndex) { - writerIndex(writerIndex() + buffer.readableBytes()); + writerIndex(writerIndex() + readableBytes); } return cIndex; } finally { @@ -663,6 +657,7 @@ public CompositeByteBuf capacity(int newCapacity) { Component c = i.previous(); if (bytesToTrim >= c.length) { bytesToTrim -= c.length; + c.freeIfNecessary(); i.remove(); continue; } @@ -1635,6 +1630,7 @@ public CompositeByteBuf discardReadBytes() { if (adjustment == c.length) { // new slice would be empty, so remove instead firstComponentId++; + c.freeIfNecessary(); } else { Component newC = new Component(c.buf.slice(adjustment, c.length - adjustment)); components.set(firstComponentId, newC); diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java index 03e55913a069..363b25d8c74c 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java @@ -1018,6 +1018,33 @@ public void testAddEmptyBufferInMiddle() { cbuf.release(); } + @Test + public void testInsertEmptyBufferInMiddle() { + CompositeByteBuf cbuf = compositeBuffer(); + ByteBuf buf1 = buffer().writeByte((byte) 1); + cbuf.addComponent(true, buf1); + ByteBuf buf2 = buffer().writeByte((byte) 2); + cbuf.addComponent(true, buf2); + + // insert empty one between the first two + cbuf.addComponent(true, 1, EMPTY_BUFFER); + + assertEquals(2, cbuf.readableBytes()); + assertEquals((byte) 1, cbuf.readByte()); + assertEquals((byte) 2, cbuf.readByte()); + + assertEquals(2, cbuf.capacity()); + assertEquals(3, cbuf.numComponents()); + + byte[] dest = new byte[2]; + // should skip over the empty one, not throw a java.lang.Error :) + cbuf.getBytes(0, dest); + + assertArrayEquals(new byte[] {1, 2}, dest); + + cbuf.release(); + } + @Test public void testIterator() { CompositeByteBuf cbuf = compositeBuffer(); @@ -1117,6 +1144,29 @@ public void testReleasesItsComponents() { assertEquals(0, buffer.refCnt()); } + @Test + public void testReleasesOnShrink() { + + ByteBuf b1 = Unpooled.buffer(2).writeShort(1); + ByteBuf b2 = Unpooled.buffer(2).writeShort(2); + + // composite takes ownership of s1 and s2 + ByteBuf composite = Unpooled.compositeBuffer() + .addComponents(b1, b2); + + assertEquals(4, composite.capacity()); + + // reduce capacity down to two, will drop the second component + composite.capacity(2); + assertEquals(2, composite.capacity()); + + // releasing composite should release the components + composite.release(); + assertEquals(0, composite.refCnt()); + assertEquals(0, b1.refCnt()); + assertEquals(0, b2.refCnt()); + } + @Test public void testAllocatorIsSameWhenCopy() { testAllocatorIsSameWhenCopy(false); From 9e5073960191d43dfa07277545f272da0c1f385a Mon Sep 17 00:00:00 2001 From: root Date: Mon, 29 Oct 2018 15:37:47 +0000 Subject: [PATCH 233/417] [maven-release-plugin] prepare release netty-4.1.31.Final --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 6 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 77 insertions(+), 73 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 64b3fe8cbc9c..a8fd6771789b 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-all diff --git a/bom/pom.xml b/bom/pom.xml index b20066b07449..fd3086874003 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.31.Final-SNAPSHOT + 4.1.31.Final pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.31.Final @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec-dns - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec-haproxy - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec-http - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec-http2 - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec-memcache - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec-mqtt - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec-redis - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec-smtp - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec-socks - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec-stomp - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-codec-xml - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-common - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-dev-tools - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-handler - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-handler-proxy - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-resolver - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-resolver-dns - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-transport - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-transport-rxtx - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-transport-sctp - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-transport-udt - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-example - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-all - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-transport-native-unix-common - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-transport-native-unix-common - 4.1.31.Final-SNAPSHOT + 4.1.31.Final linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.31.Final-SNAPSHOT + 4.1.31.Final osx-x86_64 io.netty netty-transport-native-epoll - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-transport-native-epoll - 4.1.31.Final-SNAPSHOT + 4.1.31.Final linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.31.Final-SNAPSHOT + 4.1.31.Final io.netty netty-transport-native-kqueue - 4.1.31.Final-SNAPSHOT + 4.1.31.Final osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index f45cca7af2eb..edd2221903c7 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 482711865f01..5e5e7bfae41e 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 4eca96d20b6f..b91c7e6a4bae 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 342c5cf613e0..8dc3d8b26cf7 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index f02f3159cd6f..7406c8aab076 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index ff45bf73335f..063642911584 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 1b053b17c407..bdc2b5b583b5 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 2c886ba30f69..63562ef4dfc5 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index 9273d5f19d5a..98cbca1360cd 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 1996037296dd..d37a48db02a7 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index a805e15ab92d..1a7eb3f1144f 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index 21fa7c0ce440..b44d31da888e 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index c44fe84cd4fe..d9e3c2f5d7c8 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-codec diff --git a/common/pom.xml b/common/pom.xml index 6add1d470ad8..caa65b7d950c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index c7f1cc9b67b5..2075c9ba115a 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.31.Final-SNAPSHOT + 4.1.31.Final Netty/Dev-Tools @@ -50,4 +50,8 @@ + + + netty-4.1.31.Final + diff --git a/example/pom.xml b/example/pom.xml index 567e4f041f94..b9d240b2d5af 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 26e3215501d8..5ff18c01a5c7 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index daf4dceb5826..307031b9b718 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 67e656aa6f67..079bcb1a46b5 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-microbench diff --git a/pom.xml b/pom.xml index c2b0e799f9a6..c89ea0465b78 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.31.Final-SNAPSHOT + 4.1.31.Final Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.31.Final @@ -145,7 +145,7 @@ boringssl netty-tcnative-boringssl-static - + diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index 54fca875289a..ec52e4668313 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index 249bf7088a13..56b575163884 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 1b960e67031c..2837ebb24159 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 23e0a82b9443..832eb4cd6fb1 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 53bb65d2dec2..28b49b2e393d 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index c91f06ac4c51..5803aeb961cb 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index 23ccd1d490ff..071e3906abac 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 5ffb1923b79d..77dc65284c20 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 5334f686d09c..23cf083094b3 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index a42bb0550af7..a9233f3e8df6 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 3462aabcf7dc..91afb2c14be8 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 3adad37a371a..602331a89a60 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index ff600b3cfa88..fb7ba7e7f4cf 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 5e5c29819188..95e911de4855 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 4160bc6ea24b..b322b21979d1 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 701ade2fcea0..978fa90c5c87 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final-SNAPSHOT + 4.1.31.Final netty-transport From 3e7ddb36c793eeb039fe2a842a6cd917e7d5ddc6 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 29 Oct 2018 15:38:51 +0000 Subject: [PATCH 234/417] [maven-release-plugin] prepare for next development iteration --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 72 insertions(+), 76 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index a8fd6771789b..b250a86c3aa7 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-all diff --git a/bom/pom.xml b/bom/pom.xml index fd3086874003..e1ee7ff5ef83 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.31.Final + 4.1.32.Final-SNAPSHOT pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.31.Final + HEAD @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec-dns - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec-haproxy - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec-http - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec-http2 - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec-memcache - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec-mqtt - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec-redis - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec-smtp - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec-socks - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec-stomp - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-codec-xml - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-common - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-dev-tools - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-handler - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-handler-proxy - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-resolver - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-resolver-dns - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-transport - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-transport-rxtx - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-transport-sctp - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-transport-udt - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-example - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-all - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.31.Final + 4.1.32.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.31.Final + 4.1.32.Final-SNAPSHOT osx-x86_64 io.netty netty-transport-native-epoll - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-transport-native-epoll - 4.1.31.Final + 4.1.32.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.31.Final + 4.1.32.Final-SNAPSHOT io.netty netty-transport-native-kqueue - 4.1.31.Final + 4.1.32.Final-SNAPSHOT osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index edd2221903c7..73fedbf899b2 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 5e5e7bfae41e..39567c621789 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index b91c7e6a4bae..f2d7bb210045 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 8dc3d8b26cf7..6f1efbc31ec0 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 7406c8aab076..0e713817f9cc 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 063642911584..ea1eb8273b07 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index bdc2b5b583b5..a18d1396d27c 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 63562ef4dfc5..08aa0bf62760 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index 98cbca1360cd..b67e516ce3a8 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index d37a48db02a7..28dbafbd1bdb 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 1a7eb3f1144f..ec948a6fa985 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index b44d31da888e..c8341c4306d8 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index d9e3c2f5d7c8..646c7f617cf7 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-codec diff --git a/common/pom.xml b/common/pom.xml index caa65b7d950c..dce8b4602d39 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 2075c9ba115a..65858c73df90 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.31.Final + 4.1.32.Final-SNAPSHOT Netty/Dev-Tools @@ -50,8 +50,4 @@ - - - netty-4.1.31.Final - diff --git a/example/pom.xml b/example/pom.xml index b9d240b2d5af..7d1adb27fa37 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 5ff18c01a5c7..5fef26836f4a 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index 307031b9b718..55dca310ff68 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 079bcb1a46b5..5152ccd019bd 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-microbench diff --git a/pom.xml b/pom.xml index c89ea0465b78..2830992cf972 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.31.Final + 4.1.32.Final-SNAPSHOT Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.31.Final + HEAD diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index ec52e4668313..f6be79007b4d 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index 56b575163884..1be3590be6e7 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 2837ebb24159..dbf6401f7564 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 832eb4cd6fb1..0d16cac0814c 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 28b49b2e393d..86ac300dbbb7 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 5803aeb961cb..175b4a758897 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index 071e3906abac..9e53b49513d5 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 77dc65284c20..a5a937d91ebf 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 23cf083094b3..1af08c5b4008 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index a9233f3e8df6..c2bfa5f4c77d 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 91afb2c14be8..03547827160f 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 602331a89a60..75de5014eee9 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index fb7ba7e7f4cf..f09a86f274c0 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 95e911de4855..d62d24233fe9 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index b322b21979d1..5a89dbae93bc 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 978fa90c5c87..e504fa6c3969 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.31.Final + 4.1.32.Final-SNAPSHOT netty-transport From 46460de24387f9bc3467cbed0d5c61113f402cd2 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 30 Oct 2018 08:17:31 +0100 Subject: [PATCH 235/417] Correctly init X509Certificate array when testing if we need to wrap the KeyManager due of TLSv1.3 (#8435) Motivation: 201e984cb3995d59cf8254f851f0ffb9090c2fea added support to use native TLSv1.3 support even with Java versions prior to 11. For this we try to detect if we need to wrap the used KeyManager or not. This testing code did create an X509Certificate[1] but does not correctly also set the certficiate on index 0. While this should be harmless we should better do the right thing and set it. Modifications: Correctly init the array. Result: Cleaner and more correct code. --- .../java/io/netty/handler/ssl/OpenSsl.java | 74 ++++++++++--------- ...OpenSslTlsv13X509ExtendedTrustManager.java | 2 +- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index e22457152c16..67ca5ca91fe9 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -33,6 +33,7 @@ import java.io.ByteArrayInputStream; import java.security.AccessController; import java.security.PrivilegedAction; +import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -182,39 +183,7 @@ public final class OpenSsl { logger.debug("Hostname Verification not supported."); } try { - // Bytes of self-signed certificate for netty.io - byte[] certBytes = { - 48, -126, 1, -92, 48, -126, 1, 13, -96, 3, 2, 1, 2, 2, 9, 0, -9, 61, - 44, 121, -118, -4, -45, -120, 48, 13, 6, 9, 42, -122, 72, -122, - -9, 13, 1, 1, 5, 5, 0, 48, 19, 49, 17, 48, 15, 6, 3, 85, 4, 3, 19, - 8, 110, 101, 116, 116, 121, 46, 105, 111, 48, 32, 23, 13, 49, 55, - 49, 48, 50, 48, 49, 56, 49, 54, 51, 54, 90, 24, 15, 57, 57, 57, 57, - 49, 50, 51, 49, 50, 51, 53, 57, 53, 57, 90, 48, 19, 49, 17, 48, 15, - 6, 3, 85, 4, 3, 19, 8, 110, 101, 116, 116, 121, 46, 105, 111, 48, -127, - -97, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -127, - -115, 0, 48, -127, -119, 2, -127, -127, 0, -116, 37, 122, -53, 28, 46, - 13, -90, -14, -33, 111, -108, -41, 59, 90, 124, 113, -112, -66, -17, - -102, 44, 13, 7, -33, -28, 24, -79, -126, -76, 40, 111, -126, -103, - -102, 34, 11, 45, 16, -38, 63, 24, 80, 24, 76, 88, -93, 96, 11, 38, - -19, -64, -11, 87, -49, -52, -65, 24, 36, -22, 53, 8, -42, 14, -121, - 114, 6, 17, -82, 10, 92, -91, -127, 81, -12, -75, 105, -10, -106, 91, - -38, 111, 50, 57, -97, -125, 109, 42, -87, -1, -19, 80, 78, 49, -97, -4, - 23, -2, -103, 122, -107, -43, 4, -31, -21, 90, 39, -9, -106, 34, -101, - -116, 31, -94, -84, 80, -6, -78, -33, 87, -90, 31, 103, 100, 56, -103, - -5, 11, 2, 3, 1, 0, 1, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, - 5, 5, 0, 3, -127, -127, 0, 112, 45, -73, 5, 64, 49, 59, 101, 51, 73, - -96, 62, 23, -84, 90, -41, -58, 83, -20, -72, 38, 123, -108, -45, 28, - 96, -122, -18, 30, 42, 86, 87, -87, -28, 107, 110, 11, -59, 91, 100, - 101, -18, 26, -103, -78, -80, -3, 38, 113, 83, -48, -108, 109, 41, -15, - 6, 112, 105, 7, -46, -11, -3, -51, 40, -66, -73, -83, -46, -94, -121, - -88, 51, -106, -77, 109, 53, -7, 123, 91, 75, -105, -22, 64, 121, -72, - -59, -21, -44, 84, 12, 9, 120, 21, -26, 13, 49, -81, -58, -47, 117, - -44, -18, -17, 124, 49, -48, 19, 16, -41, 71, -52, -107, 99, -19, -29, - 105, -93, -71, -38, -97, -128, -2, 118, 119, 49, -126, 109, 119 }; - - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509Certificate certificate = (X509Certificate) cf.generateCertificate( - new ByteArrayInputStream(certBytes)); + X509Certificate certificate = selfSignedCertificate(); certBio = ReferenceCountedOpenSslContext.toBIO(ByteBufAllocator.DEFAULT, certificate); SSL.setCertificateChainBio(ssl, certBio, false); supportsKeyManagerFactory = true; @@ -325,6 +294,45 @@ public Boolean run() { } } + /** + * Returns a self-signed {@link X509Certificate} for {@code netty.io}. + */ + static X509Certificate selfSignedCertificate() throws CertificateException { + // Bytes of self-signed certificate for netty.io + byte[] certBytes = { + 48, -126, 1, -92, 48, -126, 1, 13, -96, 3, 2, 1, 2, 2, 9, 0, -9, 61, + 44, 121, -118, -4, -45, -120, 48, 13, 6, 9, 42, -122, 72, -122, + -9, 13, 1, 1, 5, 5, 0, 48, 19, 49, 17, 48, 15, 6, 3, 85, 4, 3, 19, + 8, 110, 101, 116, 116, 121, 46, 105, 111, 48, 32, 23, 13, 49, 55, + 49, 48, 50, 48, 49, 56, 49, 54, 51, 54, 90, 24, 15, 57, 57, 57, 57, + 49, 50, 51, 49, 50, 51, 53, 57, 53, 57, 90, 48, 19, 49, 17, 48, 15, + 6, 3, 85, 4, 3, 19, 8, 110, 101, 116, 116, 121, 46, 105, 111, 48, -127, + -97, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -127, + -115, 0, 48, -127, -119, 2, -127, -127, 0, -116, 37, 122, -53, 28, 46, + 13, -90, -14, -33, 111, -108, -41, 59, 90, 124, 113, -112, -66, -17, + -102, 44, 13, 7, -33, -28, 24, -79, -126, -76, 40, 111, -126, -103, + -102, 34, 11, 45, 16, -38, 63, 24, 80, 24, 76, 88, -93, 96, 11, 38, + -19, -64, -11, 87, -49, -52, -65, 24, 36, -22, 53, 8, -42, 14, -121, + 114, 6, 17, -82, 10, 92, -91, -127, 81, -12, -75, 105, -10, -106, 91, + -38, 111, 50, 57, -97, -125, 109, 42, -87, -1, -19, 80, 78, 49, -97, -4, + 23, -2, -103, 122, -107, -43, 4, -31, -21, 90, 39, -9, -106, 34, -101, + -116, 31, -94, -84, 80, -6, -78, -33, 87, -90, 31, 103, 100, 56, -103, + -5, 11, 2, 3, 1, 0, 1, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, + 5, 5, 0, 3, -127, -127, 0, 112, 45, -73, 5, 64, 49, 59, 101, 51, 73, + -96, 62, 23, -84, 90, -41, -58, 83, -20, -72, 38, 123, -108, -45, 28, + 96, -122, -18, 30, 42, 86, 87, -87, -28, 107, 110, 11, -59, 91, 100, + 101, -18, 26, -103, -78, -80, -3, 38, 113, 83, -48, -108, 109, 41, -15, + 6, 112, 105, 7, -46, -11, -3, -51, 40, -66, -73, -83, -46, -94, -121, + -88, 51, -106, -77, 109, 53, -7, 123, 91, 75, -105, -22, 64, 121, -72, + -59, -21, -44, 84, 12, 9, 120, 21, -26, 13, 49, -81, -58, -47, 117, + -44, -18, -17, 124, 49, -48, 19, 16, -41, 71, -52, -107, 99, -19, -29, + 105, -93, -71, -38, -97, -128, -2, 118, 119, 49, -126, 109, 119 }; + + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + return (X509Certificate) cf.generateCertificate( + new ByteArrayInputStream(certBytes)); + } + private static boolean doesSupportOcsp() { boolean supportsOcsp = false; if (version() >= 0x10002000L) { diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java index e145c05c0875..00c6886e9ad4 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java @@ -50,8 +50,8 @@ private OpenSslTlsv13X509ExtendedTrustManager(X509ExtendedTrustManager tm) { static X509ExtendedTrustManager wrap(X509ExtendedTrustManager tm, boolean client) { if (PlatformDependent.javaVersion() < 11) { - X509Certificate[] certs = new X509Certificate[1]; try { + X509Certificate[] certs = { OpenSsl.selfSignedCertificate() }; if (client) { tm.checkServerTrusted(certs, "RSA", new DummySSLEngine(true)); } else { From 44c3b824ecabe5d4bf52d3a9a7a30c456ee391b8 Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Tue, 30 Oct 2018 03:09:27 -0600 Subject: [PATCH 236/417] Remove uninterpolated `{}` in DefaultHttp2ConnectionDecoder log message (#8441) Motivation: There are log messages emitted from Http2ConnectionDecoder of the form ``` INF i.n.h.c.h.DefaultHttp2ConnectionDecoder ignoring HEADERS frame for stream RST_STREAM sent. {} ``` Modifications: Remove the trailing `{}` in the log message that doesn't have a value. Result: Log messages no longer have a trailing `{}`. --- .../handler/codec/http2/DefaultHttp2ConnectionDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java index 9114f05ff06f..2d78fc9ca917 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java @@ -537,7 +537,7 @@ private boolean shouldIgnoreHeadersOrDataFrame(ChannelHandlerContext ctx, int st // elsewhere so we don't close the stream or otherwise modify the stream's state. if (logger.isInfoEnabled()) { - logger.info("{} ignoring {} frame for stream {} {}", ctx.channel(), frameName, + logger.info("{} ignoring {} frame for stream {}", ctx.channel(), frameName, stream.isResetSent() ? "RST_STREAM sent." : ("Stream created after GOAWAY sent. Last known stream by peer " + connection.remote().lastStreamKnownByPeer())); From f4cf674f01fb8253f86ab7f5f18c37b49e3f758b Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Tue, 30 Oct 2018 13:15:16 +0100 Subject: [PATCH 237/417] Fix NPE when trying to build a DnsNameResolver with a null resolvedAddressTypes (#8445) Motivation: It should be possible to build a DnsNameResolver with a null resolvedAddressTypes, defaulting then to DEFAULT_RESOLVE_ADDRESS_TYPES (see line 309). Sadly, `preferredAddressType` is then called on line 377 with the original parameter instead of the instance attribute, causing an NPE when it's null. Modification: Call preferredAddressType with instance attribuet instead of constructor parameter. Result: No more NPE --- .../netty/resolver/dns/DnsNameResolver.java | 2 +- .../resolver/dns/DnsNameResolverTest.java | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java index 6206c28ac663..5ab6c5285ed9 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java @@ -374,7 +374,7 @@ public DnsNameResolver( default: throw new IllegalArgumentException("Unknown ResolvedAddressTypes " + resolvedAddressTypes); } - preferredAddressType = preferredAddressType(resolvedAddressTypes); + preferredAddressType = preferredAddressType(this.resolvedAddressTypes); this.authoritativeDnsServerCache = checkNotNull(authoritativeDnsServerCache, "authoritativeDnsServerCache"); nameServerComparator = new NameServerComparator(preferredAddressType.addressType()); diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java index 2f473626a4ed..86b01c4de19b 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java @@ -2403,4 +2403,27 @@ public boolean clear(String hostname) { } } } + + @Test + public void testInstanceWithNullPreferredAddressType() { + new DnsNameResolver( + group.next(), // eventLoop + new ReflectiveChannelFactory(NioDatagramChannel.class), // channelFactory + NoopDnsCache.INSTANCE, // resolveCache + NoopAuthoritativeDnsServerCache.INSTANCE, // authoritativeDnsServerCache + NoopDnsQueryLifecycleObserverFactory.INSTANCE, // dnsQueryLifecycleObserverFactory + 100, // queryTimeoutMillis + null, // resolvedAddressTypes, see https://github.com/netty/netty/pull/8445 + true, // recursionDesired + 1, // maxQueriesPerResolve + false, // traceEnabled + 4096, // maxPayloadSize + true, // optResourceEnabled + HostsFileEntriesResolver.DEFAULT, // hostsFileEntriesResolver + DnsServerAddressStreamProviders.platformDefault(), // dnsServerAddressStreamProvider + null, // searchDomains + 1, // ndots + true // decodeIdn + ).close(); + } } From f5bfab374ed7cdd468a75aa54d26994b192ea96c Mon Sep 17 00:00:00 2001 From: sullis Date: Tue, 30 Oct 2018 06:19:27 -0700 Subject: [PATCH 238/417] Maven compiler plugin 3.8.0 (#8417) Motivation: latest version of the plugin Modification: Bump up version in pom.xml Result: Use latest plugin --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2830992cf972..339a1564b8cc 100644 --- a/pom.xml +++ b/pom.xml @@ -677,7 +677,7 @@ maven-compiler-plugin - 3.6.0 + 3.8.0 1.8 true From 52699bd6ddedc399a2d93af8f630dde2a4d2ad63 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 30 Oct 2018 17:58:08 +0100 Subject: [PATCH 239/417] Add test to verify that invalid ciphers are handled in all SSLEngine implementations correctly. (#8443) Motivation: https://github.com/netty/netty/issues/8442 reported that we fail to build a SslContext when an invalid cipher is used with netty-tcnative-boringssl-static, while it worked before. This test verifies that this is now consistent with all other SSLEngine implementations. Modifications: Add test-case to verify consistent behaviour Result: More tests to assert consistent behaviour across SSLEngine implementations --- .../io/netty/handler/ssl/SSLEngineTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index b15a1beb111a..ba2cdf2c2248 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -63,6 +63,7 @@ import java.security.Provider; import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -82,6 +83,7 @@ import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactorySpi; @@ -2637,6 +2639,28 @@ protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { } } + @Test + public void testInvalidCipher() throws Exception { + SelfSignedCertificate cert = new SelfSignedCertificate(); + List cipherList = new ArrayList(); + Collections.addAll(cipherList, ((SSLSocketFactory) SSLSocketFactory.getDefault()).getDefaultCipherSuites()); + cipherList.add("InvalidCipher"); + SSLEngine server = null; + try { + serverSslCtx = SslContextBuilder.forServer(cert.key(), cert.cert()).sslProvider(sslClientProvider()) + .ciphers(cipherList).build(); + server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + fail(); + } catch (IllegalArgumentException expected) { + // expected when invalid cipher is used. + } catch (SSLException expected) { + // expected when invalid cipher is used. + } finally { + cert.delete(); + cleanupServerSslEngine(server); + } + } + protected SSLEngine wrapEngine(SSLEngine engine) { return engine; } From 44cca1a26f5c395420111fafa122bed5aeecfeb7 Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Tue, 30 Oct 2018 11:35:39 -0700 Subject: [PATCH 240/417] Avoid allocations when wrapping byte[] and ByteBuffer arrays as ByteBuf (#8420) Motivation: Unpooled.wrap(byte[]...) and Unpooled.wrap(ByteBuffer...) currently allocate/copy an intermediate ByteBuf ArrayList and array, which can be avoided. Modifications: - Define new internal ByteWrapper interface and add a CompositeByteBuf constructor which takes a ByteWrapper with an array of the type that it wraps, and modify the appropriate Unpooled.wrap(...) methods to take advantage of it - Tidy up other constructors in CompositeByteBuf to remove duplication and misleading len arg (which is really an end offset into provided array) Result: Less allocation/copying when wrapping byte[] and ByteBuffer arrays, tidier code. --- .../io/netty/buffer/CompositeByteBuf.java | 109 ++++++++++++------ .../main/java/io/netty/buffer/Unpooled.java | 69 ++++------- 2 files changed, 97 insertions(+), 81 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index f66a4b91d51a..839737e42981 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -53,61 +53,81 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements private boolean freed; - public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents) { + private CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, int initSize) { super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY); if (alloc == null) { throw new NullPointerException("alloc"); } + if (maxNumComponents < 2) { + throw new IllegalArgumentException( + "maxNumComponents: " + maxNumComponents + " (expected: >= 2)"); + } this.alloc = alloc; this.direct = direct; this.maxNumComponents = maxNumComponents; - components = newList(0, maxNumComponents); + components = newList(initSize, maxNumComponents); + } + + public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents) { + this(alloc, direct, maxNumComponents, 0); } public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf... buffers) { this(alloc, direct, maxNumComponents, buffers, 0, buffers.length); } - CompositeByteBuf( - ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf[] buffers, int offset, int len) { - super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY); - if (alloc == null) { - throw new NullPointerException("alloc"); - } - if (maxNumComponents < 2) { - throw new IllegalArgumentException( - "maxNumComponents: " + maxNumComponents + " (expected: >= 2)"); - } + CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, + ByteBuf[] buffers, int offset, int endOffset) { + this(alloc, direct, maxNumComponents, endOffset - offset); - this.alloc = alloc; - this.direct = direct; - this.maxNumComponents = maxNumComponents; - components = newList(len, maxNumComponents); - - addComponents0(false, 0, buffers, offset, len); + addComponents0(false, 0, buffers, offset, endOffset); consolidateIfNeeded(); setIndex(0, capacity()); } public CompositeByteBuf( ByteBufAllocator alloc, boolean direct, int maxNumComponents, Iterable buffers) { - super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY); - if (alloc == null) { - throw new NullPointerException("alloc"); + this(alloc, direct, maxNumComponents, + buffers instanceof Collection ? ((Collection) buffers).size() : 0); + + addComponents0(false, 0, buffers); + consolidateIfNeeded(); + setIndex(0, capacity()); + } + + // support passing arrays of other types instead of having to copy to a ByteBuf[] first + interface ByteWrapper { + ByteBuf wrap(T bytes); + boolean isEmpty(T bytes); + } + + static final ByteWrapper BYTE_ARRAY_WRAPPER = new ByteWrapper() { + @Override + public ByteBuf wrap(byte[] bytes) { + return Unpooled.wrappedBuffer(bytes); } - if (maxNumComponents < 2) { - throw new IllegalArgumentException( - "maxNumComponents: " + maxNumComponents + " (expected: >= 2)"); + @Override + public boolean isEmpty(byte[] bytes) { + return bytes.length == 0; } + }; - int len = buffers instanceof Collection ? ((Collection) buffers).size() : 0; + static final ByteWrapper BYTE_BUFFER_WRAPPER = new ByteWrapper() { + @Override + public ByteBuf wrap(ByteBuffer bytes) { + return Unpooled.wrappedBuffer(bytes); + } + @Override + public boolean isEmpty(ByteBuffer bytes) { + return !bytes.hasRemaining(); + } + }; - this.alloc = alloc; - this.direct = direct; - this.maxNumComponents = maxNumComponents; - components = newList(len, maxNumComponents); + CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, + ByteWrapper wrapper, T[] buffers, int offset) { + this(alloc, direct, maxNumComponents, buffers.length - offset); - addComponents0(false, 0, buffers); + addComponents0(false, 0, wrapper, buffers, offset); consolidateIfNeeded(); setIndex(0, capacity()); } @@ -301,14 +321,15 @@ public CompositeByteBuf addComponents(int cIndex, ByteBuf... buffers) { return this; } - private int addComponents0(boolean increaseWriterIndex, int cIndex, ByteBuf[] buffers, int offset, int len) { + private int addComponents0(boolean increaseWriterIndex, int cIndex, + ByteBuf[] buffers, int offset, int endOffset) { checkNotNull(buffers, "buffers"); int i = offset; try { checkComponentIndex(cIndex); // No need for consolidation - while (i < len) { + while (i < endOffset) { // Increment i now to prepare for the next iteration and prevent a duplicate release (addComponent0 // will release if an exception occurs, and we also release in the finally block here). ByteBuf b = buffers[i++]; @@ -323,7 +344,7 @@ private int addComponents0(boolean increaseWriterIndex, int cIndex, ByteBuf[] bu } return cIndex; } finally { - for (; i < len; ++i) { + for (; i < endOffset; ++i) { ByteBuf b = buffers[i]; if (b != null) { try { @@ -336,6 +357,28 @@ private int addComponents0(boolean increaseWriterIndex, int cIndex, ByteBuf[] bu } } + private int addComponents0(boolean increaseWriterIndex, int cIndex, + ByteWrapper wrapper, T[] buffers, int offset) { + checkNotNull(buffers, "buffers"); + checkComponentIndex(cIndex); + + // No need for consolidation + for (int i = offset, len = buffers.length; i < len; i++) { + T b = buffers[i]; + if (b == null) { + break; + } + if (!wrapper.isEmpty(b)) { + cIndex = addComponent0(increaseWriterIndex, cIndex, wrapper.wrap(b)) + 1; + int size = components.size(); + if (cIndex > size) { + cIndex = size; + } + } + } + return cIndex; + } + /** * Add the given {@link ByteBuf}s on the specific index * diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/netty/buffer/Unpooled.java index 1c917b42c22e..08e4d1b79d67 100644 --- a/buffer/src/main/java/io/netty/buffer/Unpooled.java +++ b/buffer/src/main/java/io/netty/buffer/Unpooled.java @@ -15,15 +15,14 @@ */ package io.netty.buffer; +import io.netty.buffer.CompositeByteBuf.ByteWrapper; import io.netty.util.internal.PlatformDependent; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.charset.Charset; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; /** @@ -262,40 +261,39 @@ public static ByteBuf wrappedBuffer(ByteBuffer... buffers) { return wrappedBuffer(buffers.length, buffers); } - /** - * Creates a new big-endian composite buffer which wraps the specified - * arrays without copying them. A modification on the specified arrays' - * content will be visible to the returned buffer. - */ - public static ByteBuf wrappedBuffer(int maxNumComponents, byte[]... arrays) { - switch (arrays.length) { + static ByteBuf wrappedBuffer(int maxNumComponents, ByteWrapper wrapper, T[] array) { + switch (array.length) { case 0: break; case 1: - if (arrays[0].length != 0) { - return wrappedBuffer(arrays[0]); + if (!wrapper.isEmpty(array[0])) { + return wrapper.wrap(array[0]); } break; default: - // Get the list of the component, while guessing the byte order. - final List components = new ArrayList(arrays.length); - for (byte[] a: arrays) { - if (a == null) { - break; + for (int i = 0, len = array.length; i < len; i++) { + T bytes = array[i]; + if (bytes == null) { + return EMPTY_BUFFER; } - if (a.length > 0) { - components.add(wrappedBuffer(a)); + if (!wrapper.isEmpty(bytes)) { + return new CompositeByteBuf(ALLOC, false, maxNumComponents, wrapper, array, i); } } - - if (!components.isEmpty()) { - return new CompositeByteBuf(ALLOC, false, maxNumComponents, components); - } } return EMPTY_BUFFER; } + /** + * Creates a new big-endian composite buffer which wraps the specified + * arrays without copying them. A modification on the specified arrays' + * content will be visible to the returned buffer. + */ + public static ByteBuf wrappedBuffer(int maxNumComponents, byte[]... arrays) { + return wrappedBuffer(maxNumComponents, CompositeByteBuf.BYTE_ARRAY_WRAPPER, arrays); + } + /** * Creates a new big-endian composite buffer which wraps the readable bytes of the * specified buffers without copying them. A modification on the content @@ -336,32 +334,7 @@ public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuf... buffers) { * specified buffers will be visible to the returned buffer. */ public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuffer... buffers) { - switch (buffers.length) { - case 0: - break; - case 1: - if (buffers[0].hasRemaining()) { - return wrappedBuffer(buffers[0].order(BIG_ENDIAN)); - } - break; - default: - // Get the list of the component, while guessing the byte order. - final List components = new ArrayList(buffers.length); - for (ByteBuffer b: buffers) { - if (b == null) { - break; - } - if (b.remaining() > 0) { - components.add(wrappedBuffer(b.order(BIG_ENDIAN))); - } - } - - if (!components.isEmpty()) { - return new CompositeByteBuf(ALLOC, false, maxNumComponents, components); - } - } - - return EMPTY_BUFFER; + return wrappedBuffer(maxNumComponents, CompositeByteBuf.BYTE_BUFFER_WRAPPER, buffers); } /** From d4b1202e62e52dc9b5619e666f5b0033d8a32bc9 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 30 Oct 2018 19:38:02 +0100 Subject: [PATCH 241/417] Add testcase for epollWait(...) with negative timerfd values. (#8447) Motivation: https://github.com/netty/netty/issues/8444 reports that there is some issue with negative values passed to timerfd_settime. This test verifies that everything is working as expected. Modifications: Add testcase. Result: Test to verify expected behaviour. --- .../io/netty/channel/epoll/EpollTest.java | 49 ++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollTest.java index a2d4fb2d407a..5a9cb19a1ff7 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollTest.java @@ -15,13 +15,58 @@ */ package io.netty.channel.epoll; -import org.junit.Assert; +import io.netty.channel.unix.FileDescriptor; import org.junit.Test; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + public class EpollTest { @Test public void testIsAvailable() { - Assert.assertTrue(Epoll.isAvailable()); + assertTrue(Epoll.isAvailable()); + } + + // Testcase for https://github.com/netty/netty/issues/8444 + @Test(timeout = 5000) + public void testEpollWaitWithTimeOutMinusOne() throws Exception { + final EpollEventArray eventArray = new EpollEventArray(8); + try { + final FileDescriptor epoll = Native.newEpollCreate(); + final FileDescriptor timerFd = Native.newTimerFd(); + final FileDescriptor eventfd = Native.newEventFd(); + Native.epollCtlAdd(epoll.intValue(), timerFd.intValue(), Native.EPOLLIN); + Native.epollCtlAdd(epoll.intValue(), eventfd.intValue(), Native.EPOLLIN); + + final AtomicReference ref = new AtomicReference(); + Thread t = new Thread(new Runnable() { + @Override + public void run() { + try { + assertEquals(1, Native.epollWait(epoll, eventArray, timerFd, -1, -1)); + // This should have been woken up because of eventfd_write. + assertEquals(eventfd.intValue(), eventArray.fd(0)); + } catch (Throwable cause) { + ref.set(cause); + } + } + }); + t.start(); + t.join(1000); + assertTrue(t.isAlive()); + Native.eventFdWrite(eventfd.intValue(), 1); + + t.join(); + assertNull(ref.get()); + epoll.close(); + timerFd.close(); + eventfd.close(); + } finally { + eventArray.free(); + } } } From d533befa9617843f7b3e375e0ee4c4c89a35e16b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 2 Nov 2018 07:19:43 +0100 Subject: [PATCH 242/417] PlatformDependent.maxDirectMemory() must respect io.netty.maxDirectMemory (#8452) Motivation: In netty we use our own max direct memory limit that can be adjusted by io.netty.maxDirectMemory but we do not take this in acount when maxDirectMemory() is used. That will lead to non optimal configuration of PooledByteBufAllocator in some cases. This came up on stackoverflow: https://stackoverflow.com/questions/53097133/why-is-default-num-direct-arena-derived-from-platformdependent-maxdirectmemory Modifications: Correctly respect io.netty.maxDirectMemory and so configure PooledByteBufAllocator correctly by default. Result: Correct value for max direct memory. --- .../main/java/io/netty/util/internal/PlatformDependent.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index ee2101cbbeaf..d60c6e76e701 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -154,8 +154,8 @@ public Random current() { DIRECT_MEMORY_COUNTER = new AtomicLong(); } } - DIRECT_MEMORY_LIMIT = maxDirectMemory; logger.debug("-Dio.netty.maxDirectMemory: {} bytes", maxDirectMemory); + DIRECT_MEMORY_LIMIT = maxDirectMemory >= 1 ? maxDirectMemory : MAX_DIRECT_MEMORY; int tryAllocateUninitializedArray = SystemPropertyUtil.getInt("io.netty.uninitializedArrayAllocationThreshold", 1024); @@ -284,7 +284,7 @@ public static boolean directBufferPreferred() { * Returns the maximum memory reserved for direct buffer allocation. */ public static long maxDirectMemory() { - return MAX_DIRECT_MEMORY; + return DIRECT_MEMORY_LIMIT; } /** From 9c70dc8ba580731cb9c5a031185975384764183b Mon Sep 17 00:00:00 2001 From: Daniel Gartmann Date: Fri, 2 Nov 2018 06:20:54 +0000 Subject: [PATCH 243/417] Replaced obsolete cryptographic primitive with a modern/secure one. (#8450) Motivation: SHA1 is a broken hash function and shouldn't be used anymore (see: https://shattered.io/). Security scanning tools will raise this as an issue and it will reflect badly on netty and I, therefore, recommend to use a SHA2 hash function which is secure and won't be flagged by such tools. Modifications: Replaced insecure SHA1 based signing scheme with SHA2. Result: Modern and thus secure cryptographic primitives will be in use and won't be flagged by security scanning tools. --- .../handler/ssl/util/OpenJdkSelfSignedCertGenerator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/util/OpenJdkSelfSignedCertGenerator.java b/handler/src/main/java/io/netty/handler/ssl/util/OpenJdkSelfSignedCertGenerator.java index 07a6fb91eb5b..30d74e270540 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/OpenJdkSelfSignedCertGenerator.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/OpenJdkSelfSignedCertGenerator.java @@ -64,16 +64,16 @@ static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date info.set(X509CertInfo.VALIDITY, new CertificateValidity(notBefore, notAfter)); info.set(X509CertInfo.KEY, new CertificateX509Key(keypair.getPublic())); info.set(X509CertInfo.ALGORITHM_ID, - new CertificateAlgorithmId(new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid))); + new CertificateAlgorithmId(new AlgorithmId(AlgorithmId.sha256WithRSAEncryption_oid))); // Sign the cert to identify the algorithm that's used. X509CertImpl cert = new X509CertImpl(info); - cert.sign(key, "SHA1withRSA"); + cert.sign(key, "SHA256withRSA"); // Update the algorithm and sign again. info.set(CertificateAlgorithmId.NAME + '.' + CertificateAlgorithmId.ALGORITHM, cert.get(X509CertImpl.SIG_ALG)); cert = new X509CertImpl(info); - cert.sign(key, "SHA1withRSA"); + cert.sign(key, "SHA256withRSA"); cert.verify(keypair.getPublic()); return newSelfSignedCertificate(fqdn, key, cert); From 359390d04ccddc135b833b38e20cc7dcd3d37676 Mon Sep 17 00:00:00 2001 From: sullis Date: Fri, 2 Nov 2018 00:09:54 -0700 Subject: [PATCH 244/417] Update to maven-surefire-plugin 2.22.1 (#8418) Motivation: latest version of plugin should be used. See https://blogs.apache.org/maven/entry/apache-maven-surefire-plugin-version1 Modification: Update plugin version in pom.xml Result: n/a --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 339a1564b8cc..bde6a377ad02 100644 --- a/pom.xml +++ b/pom.xml @@ -1145,12 +1145,12 @@ maven-surefire-plugin - 2.19.1 + 2.22.1 maven-failsafe-plugin - 2.19.1 + 2.22.1 maven-clean-plugin From bde2865ef893f907fe99a140209f34af4d4396ef Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 2 Nov 2018 08:10:18 +0100 Subject: [PATCH 245/417] Make it clear that HashedWheelTimer only support millis. (#8322) Motivation: HWT does not support anything smaller then 1ms so we should make it clear that this is the case. Modifications: Log a warning if < 1ms is used. Result: Less suprising behaviour. --- .../java/io/netty/util/HashedWheelTimer.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/io/netty/util/HashedWheelTimer.java b/common/src/main/java/io/netty/util/HashedWheelTimer.java index e0afbe67cec6..e8e72b63872e 100644 --- a/common/src/main/java/io/netty/util/HashedWheelTimer.java +++ b/common/src/main/java/io/netty/util/HashedWheelTimer.java @@ -84,6 +84,7 @@ public class HashedWheelTimer implements Timer { private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger(); private static final AtomicBoolean WARNED_TOO_MANY_INSTANCES = new AtomicBoolean(); private static final int INSTANCE_COUNT_LIMIT = 64; + private static final long MILLISECOND_NANOS = TimeUnit.MILLISECONDS.toNanos(1); private static final ResourceLeakDetector leakDetector = ResourceLeakDetectorFactory.instance() .newResourceLeakDetector(HashedWheelTimer.class, 1); @@ -259,14 +260,25 @@ public HashedWheelTimer( mask = wheel.length - 1; // Convert tickDuration to nanos. - this.tickDuration = unit.toNanos(tickDuration); + long duration = unit.toNanos(tickDuration); // Prevent overflow. - if (this.tickDuration >= Long.MAX_VALUE / wheel.length) { + if (duration >= Long.MAX_VALUE / wheel.length) { throw new IllegalArgumentException(String.format( "tickDuration: %d (expected: 0 < tickDuration in nanos < %d", tickDuration, Long.MAX_VALUE / wheel.length)); } + + if (duration < MILLISECOND_NANOS) { + if (logger.isWarnEnabled()) { + logger.warn("Configured tickDuration %d smaller then %d, using 1ms.", + tickDuration, MILLISECOND_NANOS); + } + this.tickDuration = MILLISECOND_NANOS; + } else { + this.tickDuration = duration; + } + workerThread = threadFactory.newThread(worker); leak = leakDetection || !workerThread.isDaemon() ? leakDetector.track(this) : null; From 6fbb12e2c24a9beee12ed99f0b2d0a6d5b739d20 Mon Sep 17 00:00:00 2001 From: Dmitriy Dumanskiy Date: Fri, 2 Nov 2018 09:12:10 +0200 Subject: [PATCH 246/417] #7695 no need to manually release chunk during upload (#7696) Motivation: After #7527 fix there is no need to manually release chunks (HttpData) during file upload as they will be released on HttpPostRequestDecoder.destroy(). Modification: HttpUploadServer example doesn't release chunks manually (doesn't call data.release()). Result: Fixes #7695 and #7689 --- .../example/http/upload/HttpUploadServerHandler.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java index 1246a29fd44c..43f2d1399c0d 100644 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java +++ b/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java @@ -225,12 +225,8 @@ private void readHttpDataChunkByChunk() { logger.info(" 100% (FinalSize: " + partialContent.length() + ")"); partialContent = null; } - try { - // new value - writeHttpData(data); - } finally { - data.release(); - } + // new value + writeHttpData(data); } } // Check partial decoding for a FileUpload From 4760dc5c2d9d7be3a0e6088eb47f57554bf45074 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 2 Nov 2018 17:08:53 +0100 Subject: [PATCH 247/417] Don't double release ByteBuf when parsing of the X509Certificate fails (#8457) Motivation: Due a bug in our implementation we tried to release the same ByteBuf two times when we failed to parse the X509Certificate as closing the ByteBufInputStream already closed it. Modifications: - Don't close the ByteBuf when closing the ByteBufInputStream - Explicit release all ByteBufs after we are done parsing in a finally block. - Add testcase. Result: Do not produce an IllegalReferenceCountException and throw the correct CertificateException. --- .../java/io/netty/handler/ssl/SslContext.java | 9 ++++----- .../io/netty/handler/ssl/SslContextTest.java | 6 ++++++ .../handler/ssl/ec_params_unsupported.pem | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 handler/src/test/resources/io/netty/handler/ssl/ec_params_unsupported.pem diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContext.java b/handler/src/main/java/io/netty/handler/ssl/SslContext.java index 08fbda8d1f48..6c5a6c61096b 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java @@ -1082,10 +1082,9 @@ private static X509Certificate[] getCertificatesFromBuffers(ByteBuf[] certs) thr CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate[] x509Certs = new X509Certificate[certs.length]; - int i = 0; try { - for (; i < certs.length; i++) { - InputStream is = new ByteBufInputStream(certs[i], true); + for (int i = 0; i < certs.length; i++) { + InputStream is = new ByteBufInputStream(certs[i], false); try { x509Certs[i] = (X509Certificate) cf.generateCertificate(is); } finally { @@ -1098,8 +1097,8 @@ private static X509Certificate[] getCertificatesFromBuffers(ByteBuf[] certs) thr } } } finally { - for (; i < certs.length; i++) { - certs[i].release(); + for (ByteBuf buf: certs) { + buf.release(); } } return x509Certs; diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java index 1c0032885ac9..eb845d5651c0 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java @@ -23,6 +23,7 @@ import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; +import java.security.cert.CertificateException; import java.security.spec.InvalidKeySpecException; import javax.net.ssl.SSLContext; @@ -114,5 +115,10 @@ public void testSupportedCiphers() throws KeyManagementException, NoSuchAlgorith assertFalse(sslContext.cipherSuites().contains(unsupportedCipher)); } + @Test(expected = CertificateException.class) + public void test() throws CertificateException { + SslContext.toX509Certificates(new File(getClass().getResource("ec_params_unsupported.pem").getFile())); + } + protected abstract SslContext newServerContext(File crtFile, File keyFile, String pass) throws SSLException; } diff --git a/handler/src/test/resources/io/netty/handler/ssl/ec_params_unsupported.pem b/handler/src/test/resources/io/netty/handler/ssl/ec_params_unsupported.pem new file mode 100644 index 000000000000..cafaea422434 --- /dev/null +++ b/handler/src/test/resources/io/netty/handler/ssl/ec_params_unsupported.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC8TCCApagAwIBAgIJAOeu9WKx0IutMAoGCCqGSM49BAMCMFkxCzAJBgNVBAYT +AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn +aXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xODExMDEyMDAwMTha +Fw0yMDEwMzEyMDAwMThaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0 +YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMM +CWxvY2FsaG9zdDCCAUswggEDBgcqhkjOPQIBMIH3AgEBMCwGByqGSM49AQECIQD/ +////AAAAAQAAAAAAAAAAAAAAAP///////////////zBbBCD/////AAAAAQAAAAAA +AAAAAAAAAP///////////////AQgWsY12Ko6k+ez671VdpiGvGUdBrDMU7D2O848 +PifSYEsDFQDEnTYIhucEk2pmeOETnSa3gZ9+kARBBGsX0fLhLEJH+Lzm5WOkQPJ3 +A32BLeszoPShOUXYmMKWT+NC4v4af5uO5+tKfA+eFivOM1drMV7Oy7ZAaDe/UfUC +IQD/////AAAAAP//////////vOb6racXnoTzucrC/GMlUQIBAQNCAAQ3G/YXF+YE +XuASiyC1822n0iNPumHgFplF+6/veicKm+mDNA3NA/1zTRKJOyqpDdMyB9tgFrdV +zcHzw7JW+lDpo1MwUTAdBgNVHQ4EFgQUonraQIcnNMppU+GoJ6+vPbC84pEwHwYD +VR0jBBgwFoAUonraQIcnNMppU+GoJ6+vPbC84pEwDwYDVR0TAQH/BAUwAwEB/zAK +BggqhkjOPQQDAgNJADBGAiEAoIkAinhds0VvNtWdi6f+r+U8AA9rUsR1sJBzVOYD +ErACIQCMMyfEWW8d4N3q8fpZ/lWTNaionVWeZZHWjseTmafWQg== +-----END CERTIFICATE----- From 9f6ebab514f3e62dfe539ab68f813aaa2c275dc3 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 3 Nov 2018 09:29:44 +0100 Subject: [PATCH 248/417] Update to latest openjdk 12 ea release. (#8459) Motivation: We should always test against the latest EA release. Modifications: Update to openjdk 12 ea17 Result: Test against latest release --- docker/docker-compose.centos-6.112.yaml | 2 +- docker/docker-compose.centos-7.112.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.112.yaml b/docker/docker-compose.centos-6.112.yaml index c105f5abde00..a57ce0c94f47 100644 --- a/docker/docker-compose.centos-6.112.yaml +++ b/docker/docker-compose.centos-6.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "openjdk@1.12.0-16" + java_version : "openjdk@1.12.0-17" test: image: netty:centos-6-1.12 diff --git a/docker/docker-compose.centos-7.112.yaml b/docker/docker-compose.centos-7.112.yaml index 4df31de71261..5763acb2b4ac 100644 --- a/docker/docker-compose.centos-7.112.yaml +++ b/docker/docker-compose.centos-7.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "openjdk@1.12.0-16" + java_version : "openjdk@1.12.0-17" test: image: netty:centos-7-1.12 From 6563f23a9b72e0efa3b3ededd3bc2ee9911f7402 Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Sat, 3 Nov 2018 03:36:26 -0600 Subject: [PATCH 249/417] Don't swallow intermediate write failures in MessageToMessageEncoder (#8454) Motivation: If the encoder needs to flush more than one outbound message it will create a new ChannelPromise for all but the last write which will swallow failures. Modification: Use a PromiseCombiner in the case of multiple messages and the parent promise isn't the `VoidPromise`. Result: Intermediate failures are propagated to the original ChannelPromise. --- .../codec/MessageToMessageEncoder.java | 33 ++++++++++------ .../codec/MessageToMessageEncoderTest.java | 38 +++++++++++++++++++ 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java index 16eed6890196..6b47b6f8a2ae 100644 --- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java @@ -22,6 +22,7 @@ import io.netty.channel.ChannelPromise; import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCounted; +import io.netty.util.concurrent.PromiseCombiner; import io.netty.util.internal.StringUtil; import io.netty.util.internal.TypeParameterMatcher; @@ -108,28 +109,36 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) if (out != null) { final int sizeMinusOne = out.size() - 1; if (sizeMinusOne == 0) { - ctx.write(out.get(0), promise); + ctx.write(out.getUnsafe(0), promise); } else if (sizeMinusOne > 0) { // Check if we can use a voidPromise for our extra writes to reduce GC-Pressure // See https://github.com/netty/netty/issues/2525 - ChannelPromise voidPromise = ctx.voidPromise(); - boolean isVoidPromise = promise == voidPromise; - for (int i = 0; i < sizeMinusOne; i ++) { - ChannelPromise p; - if (isVoidPromise) { - p = voidPromise; - } else { - p = ctx.newPromise(); - } - ctx.write(out.getUnsafe(i), p); + if (promise == ctx.voidPromise()) { + writeVoidPromise(ctx, out); + } else { + writePromiseCombiner(ctx, out, promise); } - ctx.write(out.getUnsafe(sizeMinusOne), promise); } out.recycle(); } } } + private static void writeVoidPromise(ChannelHandlerContext ctx, CodecOutputList out) { + final ChannelPromise voidPromise = ctx.voidPromise(); + for (int i = 0; i < out.size(); i++) { + ctx.write(out.getUnsafe(i), voidPromise); + } + } + + private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, ChannelPromise promise) { + final PromiseCombiner combiner = new PromiseCombiner(); + for (int i = 0; i < out.size(); i++) { + combiner.add(ctx.write(out.getUnsafe(i))); + } + combiner.finish(promise); + } + /** * Encode from one message to an other. This method will be called for each written message that can be handled * by this encoder. diff --git a/codec/src/test/java/io/netty/handler/codec/MessageToMessageEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/MessageToMessageEncoderTest.java index 322631adf6c9..2d09725af0b6 100644 --- a/codec/src/test/java/io/netty/handler/codec/MessageToMessageEncoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/MessageToMessageEncoderTest.java @@ -15,9 +15,14 @@ */ package io.netty.handler.codec; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; import io.netty.channel.embedded.EmbeddedChannel; import org.junit.Test; +import static org.junit.Assert.*; import java.util.List; @@ -37,4 +42,37 @@ protected void encode(ChannelHandlerContext ctx, Object msg, List out) t }); channel.writeOutbound(new Object()); } + + @Test + public void testIntermediateWriteFailures() { + ChannelHandler encoder = new MessageToMessageEncoder() { + @Override + protected void encode(ChannelHandlerContext ctx, Object msg, List out) { + out.add(new Object()); + out.add(msg); + } + }; + + final Exception firstWriteException = new Exception(); + + ChannelHandler writeThrower = new ChannelOutboundHandlerAdapter() { + private boolean firstWritten; + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { + if (firstWritten) { + ctx.write(msg, promise); + } else { + firstWritten = true; + promise.setFailure(firstWriteException); + } + } + }; + + EmbeddedChannel channel = new EmbeddedChannel(writeThrower, encoder); + Object msg = new Object(); + ChannelFuture write = channel.writeAndFlush(msg); + assertSame(firstWriteException, write.cause()); + assertSame(msg, channel.readOutbound()); + assertFalse(channel.finish()); + } } From 10539f4dc738c48e8d63c46b72ca32906d7f40ec Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Sat, 3 Nov 2018 02:37:07 -0700 Subject: [PATCH 250/417] Streamline CompositeByteBuf internals (#8437) Motivation: CompositeByteBuf is a powerful and versatile abstraction, allowing for manipulation of large data without copying bytes. There is still a non-negligible cost to reading/writing however relative to "singular" ByteBufs, and this can be mostly eliminated with some rework of the internals. My use case is message modification/transformation while zero-copy proxying. For example replacing a string within a large message with one of a different length Modifications: - No longer slice added buffers and unwrap added slices - Components store target buf offset relative to position in composite buf - Less allocations, object footprint, pointer indirection, offset arithmetic - Use Component[] rather than ArrayList - Avoid pointer indirection and duplicate bounds check, more efficient backing array growth - Facilitates optimization when doing bulk-inserts - inserting n ByteBufs behind m is now O(m + n) instead of O(mn) - Avoid unnecessary casting and method call indirection via superclass - Eliminate some duplicate range/ref checks via non-checking versions of toComponentIndex and findComponent - Add simple fast-path for toComponentIndex(0); add racy cache of last-accessed Component to findComponent(int) - Override forEachByte0(...) and forEachByteDesc0(...) methods - Make use of RecyclableArrayList in nioBuffers(int, int) (in line with FasterCompositeByteBuf impl) - Modify addComponents0(boolean,int,Iterable) to use the Iterable directly rather than copy to an array first (and possibly to an ArrayList before that) - Optimize addComponents0(boolean,int,ByteBuf[],int) to not perform repeated array insertions and avoid second loop for offset updates - Simplify other logic in various places, in particular the general pattern used where a sub-range is iterated over - Add benchmarks to demonstrate some improvements While refactoring I also came across a couple of clear bugs. They are fixed in these changes but I will open another PR with unit tests and fixes to the current version. Result: Much faster creation, manipulation, and access; many fewer allocations and smaller footprint. Benchmark results to follow. --- .../java/io/netty/buffer/AbstractByteBuf.java | 12 +- .../io/netty/buffer/CompositeByteBuf.java | 1007 ++++++++++------- .../main/java/io/netty/buffer/Unpooled.java | 2 +- ...CompositeByteBufRandomAccessBenchmark.java | 118 ++ .../CompositeByteBufSequentialBenchmark.java | 132 +++ .../CompositeByteBufWriteOutBenchmark.java | 114 ++ 6 files changed, 969 insertions(+), 416 deletions(-) create mode 100644 microbench/src/main/java/io/netty/buffer/CompositeByteBufRandomAccessBenchmark.java create mode 100644 microbench/src/main/java/io/netty/buffer/CompositeByteBufSequentialBenchmark.java create mode 100644 microbench/src/main/java/io/netty/buffer/CompositeByteBufWriteOutBenchmark.java diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index 16b313a8ed94..6864cee4da44 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -46,7 +46,7 @@ public abstract class AbstractByteBuf extends ByteBuf { private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractByteBuf.class); private static final String LEGACY_PROP_CHECK_ACCESSIBLE = "io.netty.buffer.bytebuf.checkAccessible"; private static final String PROP_CHECK_ACCESSIBLE = "io.netty.buffer.checkAccessible"; - private static final boolean checkAccessible; + static final boolean checkAccessible; // accessed from CompositeByteBuf private static final String PROP_CHECK_BOUNDS = "io.netty.buffer.checkBounds"; private static final boolean checkBounds; @@ -332,12 +332,12 @@ public int ensureWritable(int minWritableBytes, boolean force) { @Override public ByteBuf order(ByteOrder endianness) { - if (endianness == null) { - throw new NullPointerException("endianness"); - } if (endianness == order()) { return this; } + if (endianness == null) { + throw new NullPointerException("endianness"); + } return newSwappedByteBuf(); } @@ -1293,7 +1293,7 @@ public int forEachByte(int index, int length, ByteProcessor processor) { } } - private int forEachByteAsc0(int start, int end, ByteProcessor processor) throws Exception { + int forEachByteAsc0(int start, int end, ByteProcessor processor) throws Exception { for (; start < end; ++start) { if (!processor.process(_getByte(start))) { return start; @@ -1325,7 +1325,7 @@ public int forEachByteDesc(int index, int length, ByteProcessor processor) { } } - private int forEachByteDesc0(int rStart, final int rEnd, ByteProcessor processor) throws Exception { + int forEachByteDesc0(int rStart, final int rEnd, ByteProcessor processor) throws Exception { for (; rStart >= rEnd; --rStart) { if (!processor.process(_getByte(rStart))) { return rStart; diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 839737e42981..77ba13fcaecf 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -15,7 +15,11 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; +import io.netty.util.IllegalReferenceCountException; +import io.netty.util.ReferenceCountUtil; import io.netty.util.internal.EmptyArrays; +import io.netty.util.internal.RecyclableArrayList; import java.io.IOException; import java.io.InputStream; @@ -26,12 +30,12 @@ import java.nio.channels.GatheringByteChannel; import java.nio.channels.ScatteringByteChannel; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.NoSuchElementException; import static io.netty.util.internal.ObjectUtil.checkNotNull; @@ -48,9 +52,11 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements private final ByteBufAllocator alloc; private final boolean direct; - private final ComponentList components; private final int maxNumComponents; + private int componentCount; + private Component[] components; // resized when needed + private boolean freed; private CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, int initSize) { @@ -65,7 +71,7 @@ private CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumCompo this.alloc = alloc; this.direct = direct; this.maxNumComponents = maxNumComponents; - components = newList(initSize, maxNumComponents); + components = newCompArray(initSize, maxNumComponents); } public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents) { @@ -73,16 +79,16 @@ public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumCompon } public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf... buffers) { - this(alloc, direct, maxNumComponents, buffers, 0, buffers.length); + this(alloc, direct, maxNumComponents, buffers, 0); } CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, - ByteBuf[] buffers, int offset, int endOffset) { - this(alloc, direct, maxNumComponents, endOffset - offset); + ByteBuf[] buffers, int offset) { + this(alloc, direct, maxNumComponents, buffers.length - offset); - addComponents0(false, 0, buffers, offset, endOffset); + addComponents0(false, 0, buffers, offset); consolidateIfNeeded(); - setIndex(0, capacity()); + setIndex0(0, capacity()); } public CompositeByteBuf( @@ -132,9 +138,9 @@ CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponent setIndex(0, capacity()); } - private static ComponentList newList(int initComponents, int maxNumComponents) { + private static Component[] newCompArray(int initComponents, int maxNumComponents) { int capacityGuess = Math.min(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, maxNumComponents); - return new ComponentList(Math.max(initComponents, capacityGuess)); + return new Component[Math.max(initComponents, capacityGuess)]; } // Special constructor used by WrappedCompositeByteBuf @@ -215,7 +221,7 @@ public CompositeByteBuf addComponent(int cIndex, ByteBuf buffer) { */ public CompositeByteBuf addComponent(boolean increaseWriterIndex, ByteBuf buffer) { checkNotNull(buffer, "buffer"); - addComponent0(increaseWriterIndex, components.size(), buffer); + addComponent0(increaseWriterIndex, componentCount, buffer); consolidateIfNeeded(); return this; } @@ -230,7 +236,8 @@ public CompositeByteBuf addComponent(boolean increaseWriterIndex, ByteBuf buffer * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}. */ public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... buffers) { - addComponents0(increaseWriterIndex, components.size(), buffers, 0, buffers.length); + checkNotNull(buffers, "buffers"); + addComponents0(increaseWriterIndex, componentCount, buffers, 0); consolidateIfNeeded(); return this; } @@ -245,7 +252,7 @@ public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... bu * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}. */ public CompositeByteBuf addComponents(boolean increaseWriterIndex, Iterable buffers) { - addComponents0(increaseWriterIndex, components.size(), buffers); + addComponents0(increaseWriterIndex, componentCount, buffers); consolidateIfNeeded(); return this; } @@ -275,20 +282,16 @@ private int addComponent0(boolean increaseWriterIndex, int cIndex, ByteBuf buffe try { checkComponentIndex(cIndex); - int readableBytes = buffer.readableBytes(); - // No need to consolidate - just add a component to the list. - @SuppressWarnings("deprecation") - Component c = new Component(buffer.order(ByteOrder.BIG_ENDIAN).slice()); - components.add(cIndex, c); + Component c = newComponent(buffer, 0); + int readableBytes = c.length(); + + addComp(cIndex, c); wasAdded = true; - if (readableBytes > 0 && cIndex < components.size() - 1) { + if (readableBytes > 0 && cIndex < componentCount - 1) { updateComponentOffsets(cIndex); - } else if (cIndex == 0) { - c.endOffset = readableBytes; - } else { - c.offset = components.get(cIndex - 1).endOffset; - c.endOffset = c.offset + readableBytes; + } else if (cIndex > 0) { + c.reposition(components[cIndex - 1].endOffset); } if (increaseWriterIndex) { writerIndex(writerIndex() + readableBytes); @@ -301,6 +304,26 @@ private int addComponent0(boolean increaseWriterIndex, int cIndex, ByteBuf buffe } } + // unwrap if already sliced + @SuppressWarnings("deprecation") + private Component newComponent(ByteBuf buf, int offset) { + if (checkAccessible && buf.refCnt() == 0) { + throw new IllegalReferenceCountException(0); + } + int srcIndex = buf.readerIndex(), len = buf.readableBytes(); + ByteBuf slice = null; + if (buf instanceof AbstractUnpooledSlicedByteBuf) { + srcIndex += ((AbstractUnpooledSlicedByteBuf) buf).idx(0); + slice = buf; + buf = buf.unwrap(); + } else if (buf instanceof PooledSlicedByteBuf) { + srcIndex += ((PooledSlicedByteBuf) buf).adjustment; + slice = buf; + buf = buf.unwrap(); + } + return new Component(buf.order(ByteOrder.BIG_ENDIAN), srcIndex, offset, len, slice); + } + /** * Add the given {@link ByteBuf}s on the specific index *

@@ -316,43 +339,40 @@ private int addComponent0(boolean increaseWriterIndex, int cIndex, ByteBuf buffe * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}. */ public CompositeByteBuf addComponents(int cIndex, ByteBuf... buffers) { - addComponents0(false, cIndex, buffers, 0, buffers.length); + checkNotNull(buffers, "buffers"); + addComponents0(false, cIndex, buffers, 0); consolidateIfNeeded(); return this; } - private int addComponents0(boolean increaseWriterIndex, int cIndex, - ByteBuf[] buffers, int offset, int endOffset) { - checkNotNull(buffers, "buffers"); - int i = offset; + private int addComponents0(boolean increaseWriterIndex, final int cIndex, ByteBuf[] buffers, int arrOffset) { + final int len = buffers.length, count = len - arrOffset; + int ci = Integer.MAX_VALUE; try { checkComponentIndex(cIndex); - - // No need for consolidation - while (i < endOffset) { - // Increment i now to prepare for the next iteration and prevent a duplicate release (addComponent0 - // will release if an exception occurs, and we also release in the finally block here). - ByteBuf b = buffers[i++]; - if (b == null) { - break; - } - cIndex = addComponent0(increaseWriterIndex, cIndex, b) + 1; - int size = components.size(); - if (cIndex > size) { - cIndex = size; - } + shiftComps(cIndex, count); // will increase componentCount + ci = cIndex; // only set this after we've shifted so that finally block logic is always correct + int nextOffset = cIndex > 0 ? components[cIndex - 1].endOffset : 0; + for (ByteBuf b; arrOffset < len && (b = buffers[arrOffset]) != null; arrOffset++, ci++) { + Component c = newComponent(b, nextOffset); + components[ci] = c; + nextOffset = c.endOffset; } - return cIndex; + return ci; } finally { - for (; i < endOffset; ++i) { - ByteBuf b = buffers[i]; - if (b != null) { - try { - b.release(); - } catch (Throwable ignored) { - // ignore + // ci is now the index following the last successfully added component + if (ci < componentCount) { + if (ci < cIndex + count) { + // we bailed early + removeCompRange(ci, cIndex + count); + for (; arrOffset < len; ++arrOffset) { + ReferenceCountUtil.safeRelease(buffers[arrOffset]); } } + updateComponentOffsets(ci); // only need to do this here for components after the added ones + } + if (increaseWriterIndex && ci > cIndex && ci <= componentCount) { + writerIndex(writerIndex() + components[ci - 1].endOffset - components[cIndex].offset); } } } @@ -370,7 +390,7 @@ private int addComponents0(boolean increaseWriterIndex, int cIndex, } if (!wrapper.isEmpty(b)) { cIndex = addComponent0(increaseWriterIndex, cIndex, wrapper.wrap(b)) + 1; - int size = components.size(); + int size = componentCount; if (cIndex > size) { cIndex = size; } @@ -398,37 +418,34 @@ public CompositeByteBuf addComponents(int cIndex, Iterable buffers) { return this; } + // TODO optimize further, similar to ByteBuf[] version + // (difference here is that we don't know *always* know precise size increase in advance, + // but we do in the most common case that the Iterable is a Collection) private int addComponents0(boolean increaseIndex, int cIndex, Iterable buffers) { if (buffers instanceof ByteBuf) { // If buffers also implements ByteBuf (e.g. CompositeByteBuf), it has to go to addComponent(ByteBuf). return addComponent0(increaseIndex, cIndex, (ByteBuf) buffers); } checkNotNull(buffers, "buffers"); + Iterator it = buffers.iterator(); + try { + checkComponentIndex(cIndex); - if (!(buffers instanceof Collection)) { - List list = new ArrayList(); - try { - for (ByteBuf b: buffers) { - list.add(b); - } - buffers = list; - } finally { - if (buffers != list) { - for (ByteBuf b: buffers) { - if (b != null) { - try { - b.release(); - } catch (Throwable ignored) { - // ignore - } - } - } + // No need for consolidation + while (it.hasNext()) { + ByteBuf b = it.next(); + if (b == null) { + break; } + cIndex = addComponent0(increaseIndex, cIndex, b) + 1; + cIndex = Math.min(cIndex, componentCount); + } + return cIndex; + } finally { + while (it.hasNext()) { + ReferenceCountUtil.safeRelease(it.next()); } } - - Collection col = (Collection) buffers; - return addComponents0(increaseIndex, cIndex, col.toArray(new ByteBuf[0]), 0 , col.size()); } /** @@ -438,63 +455,53 @@ private int addComponents0(boolean increaseIndex, int cIndex, Iterable private void consolidateIfNeeded() { // Consolidate if the number of components will exceed the allowed maximum by the current // operation. - final int numComponents = components.size(); - if (numComponents > maxNumComponents) { - final int capacity = components.get(numComponents - 1).endOffset; + int size = componentCount; + if (size > maxNumComponents) { + final int capacity = components[size - 1].endOffset; ByteBuf consolidated = allocBuffer(capacity); + lastAccessed = null; // We're not using foreach to avoid creating an iterator. - for (int i = 0; i < numComponents; i ++) { - Component c = components.get(i); - ByteBuf b = c.buf; - consolidated.writeBytes(b); - c.freeIfNecessary(); + for (int i = 0; i < size; i ++) { + components[i].transferTo(consolidated); } - Component c = new Component(consolidated); - c.endOffset = c.length; - components.clear(); - components.add(c); + + components[0] = new Component(consolidated, 0, 0, capacity, consolidated); + removeCompRange(1, size); } } private void checkComponentIndex(int cIndex) { ensureAccessible(); - if (cIndex < 0 || cIndex > components.size()) { + if (cIndex < 0 || cIndex > componentCount) { throw new IndexOutOfBoundsException(String.format( "cIndex: %d (expected: >= 0 && <= numComponents(%d))", - cIndex, components.size())); + cIndex, componentCount)); } } private void checkComponentIndex(int cIndex, int numComponents) { ensureAccessible(); - if (cIndex < 0 || cIndex + numComponents > components.size()) { + if (cIndex < 0 || cIndex + numComponents > componentCount) { throw new IndexOutOfBoundsException(String.format( "cIndex: %d, numComponents: %d " + "(expected: cIndex >= 0 && cIndex + numComponents <= totalNumComponents(%d))", - cIndex, numComponents, components.size())); + cIndex, numComponents, componentCount)); } } private void updateComponentOffsets(int cIndex) { - int size = components.size(); + int size = componentCount; if (size <= cIndex) { return; } - Component c = components.get(cIndex); - if (cIndex == 0) { - c.offset = 0; - c.endOffset = c.length; - cIndex ++; - } - - for (int i = cIndex; i < size; i ++) { - Component prev = components.get(i - 1); - Component cur = components.get(i); - cur.offset = prev.endOffset; - cur.endOffset = cur.offset + cur.length; + int nextIndex = cIndex > 0 ? components[cIndex].endOffset : 0; + for (; cIndex < size; cIndex++) { + Component c = components[cIndex]; + c.reposition(nextIndex); + nextIndex = c.endOffset; } } @@ -505,9 +512,13 @@ private void updateComponentOffsets(int cIndex) { */ public CompositeByteBuf removeComponent(int cIndex) { checkComponentIndex(cIndex); - Component comp = components.remove(cIndex); + Component comp = components[cIndex]; + removeComp(cIndex); + if (lastAccessed == comp) { + lastAccessed = null; + } comp.freeIfNecessary(); - if (comp.length > 0) { + if (comp.length() > 0) { // Only need to call updateComponentOffsets if the length was > 0 updateComponentOffsets(cIndex); } @@ -529,13 +540,16 @@ public CompositeByteBuf removeComponents(int cIndex, int numComponents) { int endIndex = cIndex + numComponents; boolean needsUpdate = false; for (int i = cIndex; i < endIndex; ++i) { - Component c = components.get(i); - if (c.length > 0) { + Component c = components[i]; + if (c.length() > 0) { needsUpdate = true; } + if (lastAccessed == c) { + lastAccessed = null; + } c.freeIfNecessary(); } - components.removeRange(cIndex, endIndex); + removeCompRange(cIndex, endIndex); if (needsUpdate) { // Only need to call updateComponentOffsets if the length was > 0 @@ -547,10 +561,59 @@ public CompositeByteBuf removeComponents(int cIndex, int numComponents) { @Override public Iterator iterator() { ensureAccessible(); - if (components.isEmpty()) { - return EMPTY_ITERATOR; + return componentCount == 0 ? EMPTY_ITERATOR : new CompositeByteBufIterator(); + } + + @Override + protected int forEachByteAsc0(int start, int end, ByteProcessor processor) throws Exception { + if (end <= start) { + return -1; } - return new CompositeByteBufIterator(); + for (int i = toComponentIndex0(start), length = end - start; length > 0; i++) { + Component c = components[i]; + if (c.offset == c.endOffset) { + continue; // empty + } + ByteBuf s = c.buf; + int localStart = c.idx(start); + int localLength = Math.min(length, c.endOffset - start); + // avoid additional checks in AbstractByteBuf case + int result = s instanceof AbstractByteBuf + ? ((AbstractByteBuf) s).forEachByteAsc0(localStart, localStart + localLength, processor) + : s.forEachByte(localStart, localLength, processor); + if (result != -1) { + return result - c.adjustment; + } + start += localLength; + length -= localLength; + } + return -1; + } + + @Override + protected int forEachByteDesc0(int rStart, int rEnd, ByteProcessor processor) throws Exception { + if (rEnd > rStart) { // rStart *and* rEnd are inclusive + return -1; + } + for (int i = toComponentIndex0(rStart), length = 1 + rStart - rEnd; length > 0; i--) { + Component c = components[i]; + if (c.offset == c.endOffset) { + continue; // empty + } + ByteBuf s = c.buf; + int localRStart = c.idx(length + rEnd); + int localLength = Math.min(length, localRStart), localIndex = localRStart - localLength; + // avoid additional checks in AbstractByteBuf case + int result = s instanceof AbstractByteBuf + ? ((AbstractByteBuf) s).forEachByteDesc0(localRStart - 1, localIndex, processor) + : s.forEachByteDesc(localIndex, localLength, processor); + + if (result != -1) { + return result - c.adjustment; + } + length -= localLength; + } + return -1; } /** @@ -562,27 +625,25 @@ public List decompose(int offset, int length) { return Collections.emptyList(); } - int componentId = toComponentIndex(offset); + int componentId = toComponentIndex0(offset); int bytesToSlice = length; // The first component - Component firstC = components.get(componentId); - int firstBufOffset = offset - firstC.offset; + Component firstC = components[componentId]; - ByteBuf slice = firstC.buf.slice(firstBufOffset + firstC.buf.readerIndex(), - Math.min(firstC.length - firstBufOffset, bytesToSlice)); + ByteBuf slice = firstC.buf.slice(firstC.idx(offset), Math.min(firstC.endOffset - offset, bytesToSlice)); bytesToSlice -= slice.readableBytes(); if (bytesToSlice == 0) { return Collections.singletonList(slice); } - List sliceList = new ArrayList(components.size() - componentId); + List sliceList = new ArrayList(componentCount - componentId); sliceList.add(slice); // Add all the slices until there is nothing more left and then return the List. do { - Component component = components.get(++componentId); - slice = component.buf.slice(component.buf.readerIndex(), Math.min(component.length, bytesToSlice)); + Component component = components[++componentId]; + slice = component.buf.slice(component.idx(component.offset), Math.min(component.length(), bytesToSlice)); bytesToSlice -= slice.readableBytes(); sliceList.add(slice); } while (bytesToSlice > 0); @@ -592,12 +653,12 @@ public List decompose(int offset, int length) { @Override public boolean isDirect() { - int size = components.size(); + int size = componentCount; if (size == 0) { return false; } for (int i = 0; i < size; i++) { - if (!components.get(i).buf.isDirect()) { + if (!components[i].buf.isDirect()) { return false; } } @@ -606,11 +667,11 @@ public boolean isDirect() { @Override public boolean hasArray() { - switch (components.size()) { + switch (componentCount) { case 0: return true; case 1: - return components.get(0).buf.hasArray(); + return components[0].buf.hasArray(); default: return false; } @@ -618,11 +679,11 @@ public boolean hasArray() { @Override public byte[] array() { - switch (components.size()) { + switch (componentCount) { case 0: return EmptyArrays.EMPTY_BYTES; case 1: - return components.get(0).buf.array(); + return components[0].buf.array(); default: throw new UnsupportedOperationException(); } @@ -630,11 +691,12 @@ public byte[] array() { @Override public int arrayOffset() { - switch (components.size()) { + switch (componentCount) { case 0: return 0; case 1: - return components.get(0).buf.arrayOffset(); + Component c = components[0]; + return c.idx(c.buf.arrayOffset()); default: throw new UnsupportedOperationException(); } @@ -642,11 +704,11 @@ public int arrayOffset() { @Override public boolean hasMemoryAddress() { - switch (components.size()) { + switch (componentCount) { case 0: return Unpooled.EMPTY_BUFFER.hasMemoryAddress(); case 1: - return components.get(0).buf.hasMemoryAddress(); + return components[0].buf.hasMemoryAddress(); default: return false; } @@ -654,11 +716,12 @@ public boolean hasMemoryAddress() { @Override public long memoryAddress() { - switch (components.size()) { + switch (componentCount) { case 0: return Unpooled.EMPTY_BUFFER.memoryAddress(); case 1: - return components.get(0).buf.memoryAddress(); + Component c = components[0]; + return c.buf.memoryAddress() + c.adjustment; default: throw new UnsupportedOperationException(); } @@ -666,52 +729,39 @@ public long memoryAddress() { @Override public int capacity() { - final int numComponents = components.size(); - if (numComponents == 0) { - return 0; - } - return components.get(numComponents - 1).endOffset; + int size = componentCount; + return size > 0 ? components[size - 1].endOffset : 0; } @Override public CompositeByteBuf capacity(int newCapacity) { checkNewCapacity(newCapacity); - int oldCapacity = capacity(); + final int size = componentCount, oldCapacity = capacity(); if (newCapacity > oldCapacity) { final int paddingLength = newCapacity - oldCapacity; - ByteBuf padding; - int nComponents = components.size(); - if (nComponents < maxNumComponents) { - padding = allocBuffer(paddingLength); - padding.setIndex(0, paddingLength); - addComponent0(false, components.size(), padding); - } else { - padding = allocBuffer(paddingLength); - padding.setIndex(0, paddingLength); + ByteBuf padding = allocBuffer(paddingLength).setIndex(0, paddingLength); + addComponent0(false, size, padding); + if (componentCount >= maxNumComponents) { // FIXME: No need to create a padding buffer and consolidate. // Just create a big single buffer and put the current content there. - addComponent0(false, components.size(), padding); consolidateIfNeeded(); } } else if (newCapacity < oldCapacity) { - int bytesToTrim = oldCapacity - newCapacity; - for (ListIterator i = components.listIterator(components.size()); i.hasPrevious();) { - Component c = i.previous(); - if (bytesToTrim >= c.length) { - bytesToTrim -= c.length; - c.freeIfNecessary(); - i.remove(); - continue; + int i = size - 1; + for (int bytesToTrim = oldCapacity - newCapacity; i >= 0; i--) { + Component c = components[i]; + final int cLength = c.length(); + if (bytesToTrim < cLength) { + // Trim the last component + c.endOffset -= bytesToTrim; + c.slice = null; + break; } - - // Replace the last component with the trimmed slice. - Component newC = new Component(c.buf.slice(0, c.length - bytesToTrim)); - newC.offset = c.offset; - newC.endOffset = newC.offset + newC.length; - i.set(newC); - break; + c.freeIfNecessary(); + bytesToTrim -= cLength; } + removeCompRange(i + 1, size); if (readerIndex() > newCapacity) { setIndex(newCapacity, newCapacity); @@ -736,7 +786,7 @@ public ByteOrder order() { * Return the current number of {@link ByteBuf}'s that are composed in this instance */ public int numComponents() { - return components.size(); + return componentCount; } /** @@ -751,10 +801,21 @@ public int maxNumComponents() { */ public int toComponentIndex(int offset) { checkIndex(offset); + return toComponentIndex(offset); + } - for (int low = 0, high = components.size(); low <= high;) { + private int toComponentIndex0(int offset) { + int size = componentCount; + if (offset == 0) { // fast-path zero offset + for (int i = 0; i < size; i++) { + if (components[i].endOffset > 0) { + return i; + } + } + } + for (int low = 0, high = size; low <= high;) { int mid = low + high >>> 1; - Component c = components.get(mid); + Component c = components[mid]; if (offset >= c.endOffset) { low = mid + 1; } else if (offset < c.offset) { @@ -769,25 +830,26 @@ public int toComponentIndex(int offset) { public int toByteIndex(int cIndex) { checkComponentIndex(cIndex); - return components.get(cIndex).offset; + return components[cIndex].offset; } @Override public byte getByte(int index) { - return _getByte(index); + Component c = findComponent(index); + return c.buf.getByte(c.idx(index)); } @Override protected byte _getByte(int index) { - Component c = findComponent(index); - return c.buf.getByte(index - c.offset); + Component c = findComponent0(index); + return c.buf.getByte(c.idx(index)); } @Override protected short _getShort(int index) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 2 <= c.endOffset) { - return c.buf.getShort(index - c.offset); + return c.buf.getShort(c.idx(index)); } else if (order() == ByteOrder.BIG_ENDIAN) { return (short) ((_getByte(index) & 0xff) << 8 | _getByte(index + 1) & 0xff); } else { @@ -797,9 +859,9 @@ protected short _getShort(int index) { @Override protected short _getShortLE(int index) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 2 <= c.endOffset) { - return c.buf.getShortLE(index - c.offset); + return c.buf.getShortLE(c.idx(index)); } else if (order() == ByteOrder.BIG_ENDIAN) { return (short) (_getByte(index) & 0xff | (_getByte(index + 1) & 0xff) << 8); } else { @@ -809,9 +871,9 @@ protected short _getShortLE(int index) { @Override protected int _getUnsignedMedium(int index) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 3 <= c.endOffset) { - return c.buf.getUnsignedMedium(index - c.offset); + return c.buf.getUnsignedMedium(c.idx(index)); } else if (order() == ByteOrder.BIG_ENDIAN) { return (_getShort(index) & 0xffff) << 8 | _getByte(index + 2) & 0xff; } else { @@ -821,9 +883,9 @@ protected int _getUnsignedMedium(int index) { @Override protected int _getUnsignedMediumLE(int index) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 3 <= c.endOffset) { - return c.buf.getUnsignedMediumLE(index - c.offset); + return c.buf.getUnsignedMediumLE(c.idx(index)); } else if (order() == ByteOrder.BIG_ENDIAN) { return _getShortLE(index) & 0xffff | (_getByte(index + 2) & 0xff) << 16; } else { @@ -833,9 +895,9 @@ protected int _getUnsignedMediumLE(int index) { @Override protected int _getInt(int index) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 4 <= c.endOffset) { - return c.buf.getInt(index - c.offset); + return c.buf.getInt(c.idx(index)); } else if (order() == ByteOrder.BIG_ENDIAN) { return (_getShort(index) & 0xffff) << 16 | _getShort(index + 2) & 0xffff; } else { @@ -845,9 +907,9 @@ protected int _getInt(int index) { @Override protected int _getIntLE(int index) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 4 <= c.endOffset) { - return c.buf.getIntLE(index - c.offset); + return c.buf.getIntLE(c.idx(index)); } else if (order() == ByteOrder.BIG_ENDIAN) { return _getShortLE(index) & 0xffff | (_getShortLE(index + 2) & 0xffff) << 16; } else { @@ -857,9 +919,9 @@ protected int _getIntLE(int index) { @Override protected long _getLong(int index) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 8 <= c.endOffset) { - return c.buf.getLong(index - c.offset); + return c.buf.getLong(c.idx(index)); } else if (order() == ByteOrder.BIG_ENDIAN) { return (_getInt(index) & 0xffffffffL) << 32 | _getInt(index + 4) & 0xffffffffL; } else { @@ -869,9 +931,9 @@ protected long _getLong(int index) { @Override protected long _getLongLE(int index) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 8 <= c.endOffset) { - return c.buf.getLongLE(index - c.offset); + return c.buf.getLongLE(c.idx(index)); } else if (order() == ByteOrder.BIG_ENDIAN) { return _getIntLE(index) & 0xffffffffL | (_getIntLE(index + 4) & 0xffffffffL) << 32; } else { @@ -886,13 +948,11 @@ public CompositeByteBuf getBytes(int index, byte[] dst, int dstIndex, int length return this; } - int i = toComponentIndex(index); + int i = toComponentIndex0(index); while (length > 0) { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); - s.getBytes(index - adjustment, dst, dstIndex, localLength); + Component c = components[i]; + int localLength = Math.min(length, c.endOffset - index); + c.buf.getBytes(c.idx(index), dst, dstIndex, localLength); index += localLength; dstIndex += localLength; length -= localLength; @@ -911,15 +971,13 @@ public CompositeByteBuf getBytes(int index, ByteBuffer dst) { return this; } - int i = toComponentIndex(index); + int i = toComponentIndex0(index); try { while (length > 0) { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); + Component c = components[i]; + int localLength = Math.min(length, c.endOffset - index); dst.limit(dst.position() + localLength); - s.getBytes(index - adjustment, dst); + c.buf.getBytes(c.idx(index), dst); index += localLength; length -= localLength; i ++; @@ -937,13 +995,11 @@ public CompositeByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int lengt return this; } - int i = toComponentIndex(index); + int i = toComponentIndex0(index); while (length > 0) { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); - s.getBytes(index - adjustment, dst, dstIndex, localLength); + Component c = components[i]; + int localLength = Math.min(length, c.endOffset - index); + c.buf.getBytes(c.idx(index), dst, dstIndex, localLength); index += localLength; dstIndex += localLength; length -= localLength; @@ -993,13 +1049,11 @@ public CompositeByteBuf getBytes(int index, OutputStream out, int length) throws return this; } - int i = toComponentIndex(index); + int i = toComponentIndex0(index); while (length > 0) { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); - s.getBytes(index - adjustment, out, localLength); + Component c = components[i]; + int localLength = Math.min(length, c.endOffset - index); + c.buf.getBytes(c.idx(index), out, localLength); index += localLength; length -= localLength; i ++; @@ -1010,25 +1064,27 @@ public CompositeByteBuf getBytes(int index, OutputStream out, int length) throws @Override public CompositeByteBuf setByte(int index, int value) { Component c = findComponent(index); - c.buf.setByte(index - c.offset, value); + c.buf.setByte(c.idx(index), value); return this; } @Override protected void _setByte(int index, int value) { - setByte(index, value); + Component c = findComponent0(index); + c.buf.setByte(c.idx(index), value); } @Override public CompositeByteBuf setShort(int index, int value) { - return (CompositeByteBuf) super.setShort(index, value); + super.setShort(index, value); + return this; } @Override protected void _setShort(int index, int value) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 2 <= c.endOffset) { - c.buf.setShort(index - c.offset, value); + c.buf.setShort(c.idx(index), value); } else if (order() == ByteOrder.BIG_ENDIAN) { _setByte(index, (byte) (value >>> 8)); _setByte(index + 1, (byte) value); @@ -1040,9 +1096,9 @@ protected void _setShort(int index, int value) { @Override protected void _setShortLE(int index, int value) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 2 <= c.endOffset) { - c.buf.setShortLE(index - c.offset, value); + c.buf.setShortLE(c.idx(index), value); } else if (order() == ByteOrder.BIG_ENDIAN) { _setByte(index, (byte) value); _setByte(index + 1, (byte) (value >>> 8)); @@ -1054,14 +1110,15 @@ protected void _setShortLE(int index, int value) { @Override public CompositeByteBuf setMedium(int index, int value) { - return (CompositeByteBuf) super.setMedium(index, value); + super.setMedium(index, value); + return this; } @Override protected void _setMedium(int index, int value) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 3 <= c.endOffset) { - c.buf.setMedium(index - c.offset, value); + c.buf.setMedium(c.idx(index), value); } else if (order() == ByteOrder.BIG_ENDIAN) { _setShort(index, (short) (value >> 8)); _setByte(index + 2, (byte) value); @@ -1073,9 +1130,9 @@ protected void _setMedium(int index, int value) { @Override protected void _setMediumLE(int index, int value) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 3 <= c.endOffset) { - c.buf.setMediumLE(index - c.offset, value); + c.buf.setMediumLE(c.idx(index), value); } else if (order() == ByteOrder.BIG_ENDIAN) { _setShortLE(index, (short) value); _setByte(index + 2, (byte) (value >>> 16)); @@ -1087,14 +1144,15 @@ protected void _setMediumLE(int index, int value) { @Override public CompositeByteBuf setInt(int index, int value) { - return (CompositeByteBuf) super.setInt(index, value); + super.setInt(index, value); + return this; } @Override protected void _setInt(int index, int value) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 4 <= c.endOffset) { - c.buf.setInt(index - c.offset, value); + c.buf.setInt(c.idx(index), value); } else if (order() == ByteOrder.BIG_ENDIAN) { _setShort(index, (short) (value >>> 16)); _setShort(index + 2, (short) value); @@ -1106,9 +1164,9 @@ protected void _setInt(int index, int value) { @Override protected void _setIntLE(int index, int value) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 4 <= c.endOffset) { - c.buf.setIntLE(index - c.offset, value); + c.buf.setIntLE(c.idx(index), value); } else if (order() == ByteOrder.BIG_ENDIAN) { _setShortLE(index, (short) value); _setShortLE(index + 2, (short) (value >>> 16)); @@ -1120,14 +1178,15 @@ protected void _setIntLE(int index, int value) { @Override public CompositeByteBuf setLong(int index, long value) { - return (CompositeByteBuf) super.setLong(index, value); + super.setLong(index, value); + return this; } @Override protected void _setLong(int index, long value) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 8 <= c.endOffset) { - c.buf.setLong(index - c.offset, value); + c.buf.setLong(c.idx(index), value); } else if (order() == ByteOrder.BIG_ENDIAN) { _setInt(index, (int) (value >>> 32)); _setInt(index + 4, (int) value); @@ -1139,9 +1198,9 @@ protected void _setLong(int index, long value) { @Override protected void _setLongLE(int index, long value) { - Component c = findComponent(index); + Component c = findComponent0(index); if (index + 8 <= c.endOffset) { - c.buf.setLongLE(index - c.offset, value); + c.buf.setLongLE(c.idx(index), value); } else if (order() == ByteOrder.BIG_ENDIAN) { _setIntLE(index, (int) value); _setIntLE(index + 4, (int) (value >>> 32)); @@ -1158,13 +1217,11 @@ public CompositeByteBuf setBytes(int index, byte[] src, int srcIndex, int length return this; } - int i = toComponentIndex(index); + int i = toComponentIndex0(index); while (length > 0) { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); - s.setBytes(index - adjustment, src, srcIndex, localLength); + Component c = components[i]; + int localLength = Math.min(length, c.endOffset - index); + c.buf.setBytes(c.idx(index), src, srcIndex, localLength); index += localLength; srcIndex += localLength; length -= localLength; @@ -1183,15 +1240,13 @@ public CompositeByteBuf setBytes(int index, ByteBuffer src) { return this; } - int i = toComponentIndex(index); + int i = toComponentIndex0(index); try { while (length > 0) { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); + Component c = components[i]; + int localLength = Math.min(length, c.endOffset - index); src.limit(src.position() + localLength); - s.setBytes(index - adjustment, src); + c.buf.setBytes(c.idx(index), src); index += localLength; length -= localLength; i ++; @@ -1209,13 +1264,11 @@ public CompositeByteBuf setBytes(int index, ByteBuf src, int srcIndex, int lengt return this; } - int i = toComponentIndex(index); + int i = toComponentIndex0(index); while (length > 0) { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); - s.setBytes(index - adjustment, src, srcIndex, localLength); + Component c = components[i]; + int localLength = Math.min(length, c.endOffset - index); + c.buf.setBytes(c.idx(index), src, srcIndex, localLength); index += localLength; srcIndex += localLength; length -= localLength; @@ -1231,20 +1284,18 @@ public int setBytes(int index, InputStream in, int length) throws IOException { return in.read(EmptyArrays.EMPTY_BYTES); } - int i = toComponentIndex(index); + int i = toComponentIndex0(index); int readBytes = 0; do { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); + Component c = components[i]; + int localLength = Math.min(length, c.endOffset - index); if (localLength == 0) { // Skip empty buffer i++; continue; } - int localReadBytes = s.setBytes(index - adjustment, in, localLength); + int localReadBytes = c.buf.setBytes(c.idx(index), in, localLength); if (localReadBytes < 0) { if (readBytes == 0) { return -1; @@ -1275,19 +1326,17 @@ public int setBytes(int index, ScatteringByteChannel in, int length) throws IOEx return in.read(EMPTY_NIO_BUFFER); } - int i = toComponentIndex(index); + int i = toComponentIndex0(index); int readBytes = 0; do { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); + Component c = components[i]; + int localLength = Math.min(length, c.endOffset - index); if (localLength == 0) { // Skip empty buffer i++; continue; } - int localReadBytes = s.setBytes(index - adjustment, in, localLength); + int localReadBytes = c.buf.setBytes(c.idx(index), in, localLength); if (localReadBytes == 0) { break; @@ -1323,19 +1372,17 @@ public int setBytes(int index, FileChannel in, long position, int length) throws return in.read(EMPTY_NIO_BUFFER, position); } - int i = toComponentIndex(index); + int i = toComponentIndex0(index); int readBytes = 0; do { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); + Component c = components[i]; + int localLength = Math.min(length, c.endOffset - index); if (localLength == 0) { // Skip empty buffer i++; continue; } - int localReadBytes = s.setBytes(index - adjustment, in, position + readBytes, localLength); + int localReadBytes = c.buf.setBytes(c.idx(index), in, position + readBytes, localLength); if (localReadBytes == 0) { break; @@ -1369,7 +1416,7 @@ public ByteBuf copy(int index, int length) { checkIndex(index, length); ByteBuf dst = allocBuffer(length); if (length != 0) { - copyTo(index, length, toComponentIndex(index), dst); + copyTo(index, length, toComponentIndex0(index), dst); } return dst; } @@ -1379,11 +1426,9 @@ private void copyTo(int index, int length, int componentId, ByteBuf dst) { int i = componentId; while (length > 0) { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); - s.getBytes(index - adjustment, dst, dstIndex, localLength); + Component c = components[i]; + int localLength = Math.min(length, c.endOffset - index); + c.buf.getBytes(c.idx(index), dst, dstIndex, localLength); index += localLength; dstIndex += localLength; length -= localLength; @@ -1400,7 +1445,8 @@ private void copyTo(int index, int length, int componentId, ByteBuf dst) { * @return buf the {@link ByteBuf} on the specified index */ public ByteBuf component(int cIndex) { - return internalComponent(cIndex).duplicate(); + checkComponentIndex(cIndex); + return components[cIndex].duplicate(); } /** @@ -1410,7 +1456,7 @@ public ByteBuf component(int cIndex) { * @return the {@link ByteBuf} on the specified index */ public ByteBuf componentAtOffset(int offset) { - return internalComponentAtOffset(offset).duplicate(); + return findComponent(offset).duplicate(); } /** @@ -1421,7 +1467,7 @@ public ByteBuf componentAtOffset(int offset) { */ public ByteBuf internalComponent(int cIndex) { checkComponentIndex(cIndex); - return components.get(cIndex).buf; + return components[cIndex].slice(); } /** @@ -1431,21 +1477,40 @@ public ByteBuf internalComponent(int cIndex) { * @param offset the offset for which the {@link ByteBuf} should be returned */ public ByteBuf internalComponentAtOffset(int offset) { - return findComponent(offset).buf; + return findComponent(offset).slice(); } + // weak cache - check it first when looking for component + private Component lastAccessed; + private Component findComponent(int offset) { + Component la = lastAccessed; + if (la != null && offset >= la.offset && offset < la.endOffset) { + ensureAccessible(); + return la; + } checkIndex(offset); + return findIt(offset); + } + + private Component findComponent0(int offset) { + Component la = lastAccessed; + if (la != null && offset >= la.offset && offset < la.endOffset) { + return la; + } + return findIt(offset); + } - for (int low = 0, high = components.size(); low <= high;) { + private Component findIt(int offset) { + for (int low = 0, high = componentCount; low <= high;) { int mid = low + high >>> 1; - Component c = components.get(mid); + Component c = components[mid]; if (offset >= c.endOffset) { low = mid + 1; } else if (offset < c.offset) { high = mid - 1; } else { - assert c.length != 0; + lastAccessed = c; return c; } } @@ -1455,17 +1520,16 @@ private Component findComponent(int offset) { @Override public int nioBufferCount() { - switch (components.size()) { + int size = componentCount; + switch (size) { case 0: return 1; case 1: - return components.get(0).buf.nioBufferCount(); + return components[0].buf.nioBufferCount(); default: int count = 0; - int componentsCount = components.size(); - for (int i = 0; i < componentsCount; i++) { - Component c = components.get(i); - count += c.buf.nioBufferCount(); + for (int i = 0; i < size; i++) { + count += components[i].buf.nioBufferCount(); } return count; } @@ -1473,11 +1537,12 @@ public int nioBufferCount() { @Override public ByteBuffer internalNioBuffer(int index, int length) { - switch (components.size()) { + switch (componentCount) { case 0: return EMPTY_NIO_BUFFER; case 1: - return components.get(0).buf.internalNioBuffer(index, length); + Component c = components[0]; + return c.buf.internalNioBuffer(c.idx(index), length); default: throw new UnsupportedOperationException(); } @@ -1487,13 +1552,14 @@ public ByteBuffer internalNioBuffer(int index, int length) { public ByteBuffer nioBuffer(int index, int length) { checkIndex(index, length); - switch (components.size()) { + switch (componentCount) { case 0: return EMPTY_NIO_BUFFER; case 1: - ByteBuf buf = components.get(0).buf; + Component c = components[0]; + ByteBuf buf = c.buf; if (buf.nioBufferCount() == 1) { - return components.get(0).buf.nioBuffer(index, length); + return buf.nioBuffer(c.idx(index), length); } } @@ -1519,29 +1585,32 @@ public ByteBuffer[] nioBuffers(int index, int length) { return new ByteBuffer[] { EMPTY_NIO_BUFFER }; } - List buffers = new ArrayList(components.size()); - int i = toComponentIndex(index); - while (length > 0) { - Component c = components.get(i); - ByteBuf s = c.buf; - int adjustment = c.offset; - int localLength = Math.min(length, s.capacity() - (index - adjustment)); - switch (s.nioBufferCount()) { + RecyclableArrayList buffers = RecyclableArrayList.newInstance(componentCount); + try { + int i = toComponentIndex0(index); + while (length > 0) { + Component c = components[i]; + ByteBuf s = c.buf; + int localLength = Math.min(length, c.endOffset - index); + switch (s.nioBufferCount()) { case 0: throw new UnsupportedOperationException(); case 1: - buffers.add(s.nioBuffer(index - adjustment, localLength)); + buffers.add(s.nioBuffer(c.idx(index), localLength)); break; default: - Collections.addAll(buffers, s.nioBuffers(index - adjustment, localLength)); + Collections.addAll(buffers, s.nioBuffers(c.idx(index), localLength)); + } + + index += localLength; + length -= localLength; + i ++; } - index += localLength; - length -= localLength; - i ++; + return buffers.toArray(new ByteBuffer[0]); + } finally { + buffers.recycle(); } - - return buffers.toArray(new ByteBuffer[0]); } /** @@ -1549,25 +1618,20 @@ public ByteBuffer[] nioBuffers(int index, int length) { */ public CompositeByteBuf consolidate() { ensureAccessible(); - final int numComponents = numComponents(); + final int numComponents = componentCount; if (numComponents <= 1) { return this; } - final Component last = components.get(numComponents - 1); - final int capacity = last.endOffset; + final int capacity = components[numComponents - 1].endOffset; final ByteBuf consolidated = allocBuffer(capacity); for (int i = 0; i < numComponents; i ++) { - Component c = components.get(i); - ByteBuf b = c.buf; - consolidated.writeBytes(b); - c.freeIfNecessary(); + components[i].transferTo(consolidated); } - - components.clear(); - components.add(new Component(consolidated)); - updateComponentOffsets(0); + lastAccessed = null; + components[0] = new Component(consolidated, 0, 0, capacity, consolidated); + removeCompRange(1, numComponents); return this; } @@ -1584,19 +1648,16 @@ public CompositeByteBuf consolidate(int cIndex, int numComponents) { } final int endCIndex = cIndex + numComponents; - final Component last = components.get(endCIndex - 1); - final int capacity = last.endOffset - components.get(cIndex).offset; + final Component last = components[endCIndex - 1]; + final int capacity = last.endOffset - components[cIndex].offset; final ByteBuf consolidated = allocBuffer(capacity); for (int i = cIndex; i < endCIndex; i ++) { - Component c = components.get(i); - ByteBuf b = c.buf; - consolidated.writeBytes(b); - c.freeIfNecessary(); + components[i].transferTo(consolidated); } - - components.removeRange(cIndex + 1, endCIndex); - components.set(cIndex, new Component(consolidated)); + lastAccessed = null; + removeCompRange(cIndex + 1, endCIndex); + components[cIndex] = new Component(consolidated, 0, 0, capacity, consolidated); updateComponentOffsets(cIndex); return this; } @@ -1614,25 +1675,26 @@ public CompositeByteBuf discardReadComponents() { // Discard everything if (readerIndex = writerIndex = capacity). int writerIndex = writerIndex(); if (readerIndex == writerIndex && writerIndex == capacity()) { - int size = components.size(); - for (int i = 0; i < size; i++) { - components.get(i).freeIfNecessary(); + for (int i = 0, size = componentCount; i < size; i++) { + components[i].freeIfNecessary(); } - components.clear(); + lastAccessed = null; + clearComps(); setIndex(0, 0); adjustMarkers(readerIndex); return this; } // Remove read components. - int firstComponentId = toComponentIndex(readerIndex); + int firstComponentId = toComponentIndex0(readerIndex); for (int i = 0; i < firstComponentId; i ++) { - components.get(i).freeIfNecessary(); + components[i].freeIfNecessary(); } - components.removeRange(0, firstComponentId); + lastAccessed = null; + removeCompRange(0, firstComponentId); // Update indexes and markers. - Component first = components.get(0); + Component first = components[0]; int offset = first.offset; updateComponentOffsets(0); setIndex(readerIndex - offset, writerIndex - offset); @@ -1651,35 +1713,43 @@ public CompositeByteBuf discardReadBytes() { // Discard everything if (readerIndex = writerIndex = capacity). int writerIndex = writerIndex(); if (readerIndex == writerIndex && writerIndex == capacity()) { - int size = components.size(); - for (int i = 0; i < size; i++) { - components.get(i).freeIfNecessary(); + for (int i = 0, size = componentCount; i < size; i++) { + components[i].freeIfNecessary(); } - components.clear(); + lastAccessed = null; + clearComps(); setIndex(0, 0); adjustMarkers(readerIndex); return this; } // Remove read components. - int firstComponentId = toComponentIndex(readerIndex); + int firstComponentId = toComponentIndex0(readerIndex); for (int i = 0; i < firstComponentId; i ++) { - components.get(i).freeIfNecessary(); + Component c = components[i]; + c.freeIfNecessary(); + if (lastAccessed == c) { + lastAccessed = null; + } } // Remove or replace the first readable component with a new slice. - Component c = components.get(firstComponentId); - int adjustment = readerIndex - c.offset; - if (adjustment == c.length) { + Component c = components[firstComponentId]; + if (readerIndex == c.endOffset) { // new slice would be empty, so remove instead - firstComponentId++; c.freeIfNecessary(); + if (lastAccessed == c) { + lastAccessed = null; + } + firstComponentId++; } else { - Component newC = new Component(c.buf.slice(adjustment, c.length - adjustment)); - components.set(firstComponentId, newC); + c.offset = 0; + c.endOffset -= readerIndex; + c.adjustment += readerIndex; + c.slice = null; } - components.removeRange(0, firstComponentId); + removeCompRange(0, firstComponentId); // Update indexes and markers. updateComponentOffsets(0); @@ -1696,253 +1766,329 @@ private ByteBuf allocBuffer(int capacity) { public String toString() { String result = super.toString(); result = result.substring(0, result.length() - 1); - return result + ", components=" + components.size() + ')'; + return result + ", components=" + componentCount + ')'; } private static final class Component { final ByteBuf buf; - final int length; + int adjustment; int offset; int endOffset; - Component(ByteBuf buf) { + private ByteBuf slice; // cached slice, may be null + + Component(ByteBuf buf, int srcOffset, int offset, int len, ByteBuf slice) { this.buf = buf; - length = buf.readableBytes(); + this.offset = offset; + this.endOffset = offset + len; + this.adjustment = srcOffset - offset; + this.slice = slice; + } + + int idx(int index) { + return index + adjustment; + } + + int length() { + return endOffset - offset; + } + + void reposition(int newOffset) { + int move = newOffset - offset; + endOffset += move; + adjustment -= move; + offset = newOffset; + } + + // copy then release + void transferTo(ByteBuf dst) { + dst.writeBytes(buf, idx(offset), length()); + freeIfNecessary(); + } + + ByteBuf slice() { + return slice != null ? slice : (slice = buf.slice(idx(offset), length())); + } + + ByteBuf duplicate() { + return buf.duplicate().setIndex(idx(offset), idx(endOffset)); } void freeIfNecessary() { buf.release(); // We should not get a NPE here. If so, it must be a bug. + slice = null; } } @Override public CompositeByteBuf readerIndex(int readerIndex) { - return (CompositeByteBuf) super.readerIndex(readerIndex); + super.readerIndex(readerIndex); + return this; } @Override public CompositeByteBuf writerIndex(int writerIndex) { - return (CompositeByteBuf) super.writerIndex(writerIndex); + super.writerIndex(writerIndex); + return this; } @Override public CompositeByteBuf setIndex(int readerIndex, int writerIndex) { - return (CompositeByteBuf) super.setIndex(readerIndex, writerIndex); + super.setIndex(readerIndex, writerIndex); + return this; } @Override public CompositeByteBuf clear() { - return (CompositeByteBuf) super.clear(); + super.clear(); + return this; } @Override public CompositeByteBuf markReaderIndex() { - return (CompositeByteBuf) super.markReaderIndex(); + super.markReaderIndex(); + return this; } @Override public CompositeByteBuf resetReaderIndex() { - return (CompositeByteBuf) super.resetReaderIndex(); + super.resetReaderIndex(); + return this; } @Override public CompositeByteBuf markWriterIndex() { - return (CompositeByteBuf) super.markWriterIndex(); + super.markWriterIndex(); + return this; } @Override public CompositeByteBuf resetWriterIndex() { - return (CompositeByteBuf) super.resetWriterIndex(); + super.resetWriterIndex(); + return this; } @Override public CompositeByteBuf ensureWritable(int minWritableBytes) { - return (CompositeByteBuf) super.ensureWritable(minWritableBytes); + super.ensureWritable(minWritableBytes); + return this; } @Override public CompositeByteBuf getBytes(int index, ByteBuf dst) { - return (CompositeByteBuf) super.getBytes(index, dst); + return getBytes(index, dst, dst.writableBytes()); } @Override public CompositeByteBuf getBytes(int index, ByteBuf dst, int length) { - return (CompositeByteBuf) super.getBytes(index, dst, length); + getBytes(index, dst, dst.writerIndex(), length); + dst.writerIndex(dst.writerIndex() + length); + return this; } @Override public CompositeByteBuf getBytes(int index, byte[] dst) { - return (CompositeByteBuf) super.getBytes(index, dst); + return getBytes(index, dst, 0, dst.length); } @Override public CompositeByteBuf setBoolean(int index, boolean value) { - return (CompositeByteBuf) super.setBoolean(index, value); + return setByte(index, value? 1 : 0); } @Override public CompositeByteBuf setChar(int index, int value) { - return (CompositeByteBuf) super.setChar(index, value); + return setShort(index, value); } @Override public CompositeByteBuf setFloat(int index, float value) { - return (CompositeByteBuf) super.setFloat(index, value); + return setInt(index, Float.floatToRawIntBits(value)); } @Override public CompositeByteBuf setDouble(int index, double value) { - return (CompositeByteBuf) super.setDouble(index, value); + return setLong(index, Double.doubleToRawLongBits(value)); } @Override public CompositeByteBuf setBytes(int index, ByteBuf src) { - return (CompositeByteBuf) super.setBytes(index, src); + super.setBytes(index, src, src.readableBytes()); + return this; } @Override public CompositeByteBuf setBytes(int index, ByteBuf src, int length) { - return (CompositeByteBuf) super.setBytes(index, src, length); + super.setBytes(index, src, length); + return this; } @Override public CompositeByteBuf setBytes(int index, byte[] src) { - return (CompositeByteBuf) super.setBytes(index, src); + return setBytes(index, src, 0, src.length); } @Override public CompositeByteBuf setZero(int index, int length) { - return (CompositeByteBuf) super.setZero(index, length); + super.setZero(index, length); + return this; } @Override public CompositeByteBuf readBytes(ByteBuf dst) { - return (CompositeByteBuf) super.readBytes(dst); + super.readBytes(dst, dst.writableBytes()); + return this; } @Override public CompositeByteBuf readBytes(ByteBuf dst, int length) { - return (CompositeByteBuf) super.readBytes(dst, length); + super.readBytes(dst, length); + return this; } @Override public CompositeByteBuf readBytes(ByteBuf dst, int dstIndex, int length) { - return (CompositeByteBuf) super.readBytes(dst, dstIndex, length); + super.readBytes(dst, dstIndex, length); + return this; } @Override public CompositeByteBuf readBytes(byte[] dst) { - return (CompositeByteBuf) super.readBytes(dst); + super.readBytes(dst, 0, dst.length); + return this; } @Override public CompositeByteBuf readBytes(byte[] dst, int dstIndex, int length) { - return (CompositeByteBuf) super.readBytes(dst, dstIndex, length); + super.readBytes(dst, dstIndex, length); + return this; } @Override public CompositeByteBuf readBytes(ByteBuffer dst) { - return (CompositeByteBuf) super.readBytes(dst); + super.readBytes(dst); + return this; } @Override public CompositeByteBuf readBytes(OutputStream out, int length) throws IOException { - return (CompositeByteBuf) super.readBytes(out, length); + super.readBytes(out, length); + return this; } @Override public CompositeByteBuf skipBytes(int length) { - return (CompositeByteBuf) super.skipBytes(length); + super.skipBytes(length); + return this; } @Override public CompositeByteBuf writeBoolean(boolean value) { - return (CompositeByteBuf) super.writeBoolean(value); + writeByte(value ? 1 : 0); + return this; } @Override public CompositeByteBuf writeByte(int value) { - return (CompositeByteBuf) super.writeByte(value); + ensureWritable0(1); + _setByte(writerIndex++, value); + return this; } @Override public CompositeByteBuf writeShort(int value) { - return (CompositeByteBuf) super.writeShort(value); + super.writeShort(value); + return this; } @Override public CompositeByteBuf writeMedium(int value) { - return (CompositeByteBuf) super.writeMedium(value); + super.writeMedium(value); + return this; } @Override public CompositeByteBuf writeInt(int value) { - return (CompositeByteBuf) super.writeInt(value); + super.writeInt(value); + return this; } @Override public CompositeByteBuf writeLong(long value) { - return (CompositeByteBuf) super.writeLong(value); + super.writeLong(value); + return this; } @Override public CompositeByteBuf writeChar(int value) { - return (CompositeByteBuf) super.writeChar(value); + super.writeShort(value); + return this; } @Override public CompositeByteBuf writeFloat(float value) { - return (CompositeByteBuf) super.writeFloat(value); + super.writeInt(Float.floatToRawIntBits(value)); + return this; } @Override public CompositeByteBuf writeDouble(double value) { - return (CompositeByteBuf) super.writeDouble(value); + super.writeLong(Double.doubleToRawLongBits(value)); + return this; } @Override public CompositeByteBuf writeBytes(ByteBuf src) { - return (CompositeByteBuf) super.writeBytes(src); + super.writeBytes(src, src.readableBytes()); + return this; } @Override public CompositeByteBuf writeBytes(ByteBuf src, int length) { - return (CompositeByteBuf) super.writeBytes(src, length); + super.writeBytes(src, length); + return this; } @Override public CompositeByteBuf writeBytes(ByteBuf src, int srcIndex, int length) { - return (CompositeByteBuf) super.writeBytes(src, srcIndex, length); + super.writeBytes(src, srcIndex, length); + return this; } @Override public CompositeByteBuf writeBytes(byte[] src) { - return (CompositeByteBuf) super.writeBytes(src); + writeBytes(src, 0, src.length); + return this; } @Override public CompositeByteBuf writeBytes(byte[] src, int srcIndex, int length) { - return (CompositeByteBuf) super.writeBytes(src, srcIndex, length); + super.writeBytes(src, srcIndex, length); + return this; } @Override public CompositeByteBuf writeBytes(ByteBuffer src) { - return (CompositeByteBuf) super.writeBytes(src); + super.writeBytes(src); + return this; } @Override public CompositeByteBuf writeZero(int length) { - return (CompositeByteBuf) super.writeZero(length); + super.writeZero(length); + return this; } @Override public CompositeByteBuf retain(int increment) { - return (CompositeByteBuf) super.retain(increment); + super.retain(increment); + return this; } @Override public CompositeByteBuf retain() { - return (CompositeByteBuf) super.retain(); + super.retain(); + return this; } @Override @@ -1972,11 +2118,10 @@ protected void deallocate() { } freed = true; - int size = components.size(); // We're not using foreach to avoid creating an iterator. // see https://github.com/netty/netty/issues/2642 - for (int i = 0; i < size; i++) { - components.get(i).freeIfNecessary(); + for (int i = 0, size = componentCount; i < size; i++) { + components[i].freeIfNecessary(); } } @@ -1986,7 +2131,7 @@ public ByteBuf unwrap() { } private final class CompositeByteBufIterator implements Iterator { - private final int size = components.size(); + private final int size = numComponents(); private int index; @Override @@ -1996,14 +2141,14 @@ public boolean hasNext() { @Override public ByteBuf next() { - if (size != components.size()) { + if (size != numComponents()) { throw new ConcurrentModificationException(); } if (!hasNext()) { throw new NoSuchElementException(); } try { - return components.get(index++).buf; + return components[index++].slice(); } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } @@ -2015,16 +2160,60 @@ public void remove() { } } - private static final class ComponentList extends ArrayList { + // Component array manipulation - range checking omitted + + private void clearComps() { + removeCompRange(0, componentCount); + } + + private void removeComp(int i) { + removeCompRange(i, i + 1); + } - ComponentList(int initialCapacity) { - super(initialCapacity); + private void removeCompRange(int from, int to) { + if (from >= to) { + return; + } + final int size = componentCount; + assert from >= 0 && to <= size; + if (to < size) { + System.arraycopy(components, to, components, from, size - to); } + int newSize = size - to + from; + for (int i = newSize; i < size; i++) { + components[i].slice = null; + components[i] = null; + } + componentCount = newSize; + } - // Expose this methods so we not need to create a new subList just to remove a range of elements. - @Override - public void removeRange(int fromIndex, int toIndex) { - super.removeRange(fromIndex, toIndex); + private void addComp(int i, Component c) { + shiftComps(i, 1); + components[i] = c; + } + + private void shiftComps(int i, int count) { + final int size = componentCount, newSize = size + count; + assert i >= 0 && i <= size && count > 0; + if (newSize > components.length) { + // grow the array + int newArrSize = Math.max(size + (size >> 1), newSize); + Component[] newArr; + if (i == size) { + newArr = Arrays.copyOf(components, newArrSize, Component[].class); + } else { + newArr = new Component[newArrSize]; + if (i > 0) { + System.arraycopy(components, 0, newArr, 0, i); + } + if (i < size) { + System.arraycopy(components, i, newArr, i + count, size - i); + } + } + components = newArr; + } else if (i < size) { + System.arraycopy(components, i, components, i + count, size - i); } + componentCount = newSize; } } diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/netty/buffer/Unpooled.java index 08e4d1b79d67..8c7dc77e564b 100644 --- a/buffer/src/main/java/io/netty/buffer/Unpooled.java +++ b/buffer/src/main/java/io/netty/buffer/Unpooled.java @@ -319,7 +319,7 @@ public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuf... buffers) { for (int i = 0; i < buffers.length; i++) { ByteBuf buf = buffers[i]; if (buf.isReadable()) { - return new CompositeByteBuf(ALLOC, false, maxNumComponents, buffers, i, buffers.length); + return new CompositeByteBuf(ALLOC, false, maxNumComponents, buffers, i); } buf.release(); } diff --git a/microbench/src/main/java/io/netty/buffer/CompositeByteBufRandomAccessBenchmark.java b/microbench/src/main/java/io/netty/buffer/CompositeByteBufRandomAccessBenchmark.java new file mode 100644 index 000000000000..bb2d9283f0f9 --- /dev/null +++ b/microbench/src/main/java/io/netty/buffer/CompositeByteBufRandomAccessBenchmark.java @@ -0,0 +1,118 @@ +/* + * Copyright 2018 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: + * + * http://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.buffer; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import io.netty.microbench.util.AbstractMicrobenchmark; + +import static io.netty.buffer.Unpooled.EMPTY_BUFFER; +import static io.netty.buffer.Unpooled.wrappedBuffer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +public class CompositeByteBufRandomAccessBenchmark extends AbstractMicrobenchmark { + + public enum ByteBufType { + SMALL_CHUNKS { + @Override + ByteBuf newBuffer(int length) { + return newBufferSmallChunks(length); + } + }, + LARGE_CHUNKS { + @Override + ByteBuf newBuffer(int length) { + return newBufferLargeChunks(length); + } + }; + abstract ByteBuf newBuffer(int length); + } + + @Param({ "64", "10240", "1024000" }) // ({ "64", "1024", "10240", "102400", "1024000" }) + public int size; + + @Param + public ByteBufType bufferType; + + private ByteBuf buffer; + private Random random; + + @Setup + public void setup() { + buffer = bufferType.newBuffer(size); + random = new Random(0L); + } + + @TearDown + public void teardown() { + buffer.release(); + } + + @Benchmark + public long setGetLong() { + int i = random.nextInt(size - 8); + return buffer.setLong(i, 1).getLong(i); + } + + @Benchmark + public ByteBuf setLong() { + int i = random.nextInt(size - 8); + return buffer.setLong(i, 1); + } + + private static ByteBuf newBufferSmallChunks(int length) { + + List buffers = new ArrayList(((length + 1) / 45) * 19); + for (int i = 0; i < length + 45; i += 45) { + for (int j = 1; j <= 9; j++) { + buffers.add(EMPTY_BUFFER); + buffers.add(wrappedBuffer(new byte[j])); + } + buffers.add(EMPTY_BUFFER); + } + + ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[0])); + + // Truncate to the requested capacity. + return buffer.capacity(length).writerIndex(0); + } + + private static ByteBuf newBufferLargeChunks(int length) { + + List buffers = new ArrayList((length + 1) / 512); + for (int i = 0; i < length + 1536; i += 1536) { + buffers.add(wrappedBuffer(new byte[512])); + buffers.add(EMPTY_BUFFER); + buffers.add(wrappedBuffer(new byte[1024])); + } + + ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[0])); + + // Truncate to the requested capacity. + return buffer.capacity(length).writerIndex(0); + } +} diff --git a/microbench/src/main/java/io/netty/buffer/CompositeByteBufSequentialBenchmark.java b/microbench/src/main/java/io/netty/buffer/CompositeByteBufSequentialBenchmark.java new file mode 100644 index 000000000000..9ba6f19aa825 --- /dev/null +++ b/microbench/src/main/java/io/netty/buffer/CompositeByteBufSequentialBenchmark.java @@ -0,0 +1,132 @@ +/* + * Copyright 2018 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: + * + * http://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.buffer; + +import io.netty.microbench.util.AbstractMicrobenchmark; +import io.netty.util.ByteProcessor; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import static io.netty.buffer.Unpooled.EMPTY_BUFFER; +import static io.netty.buffer.Unpooled.wrappedBuffer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +public class CompositeByteBufSequentialBenchmark extends AbstractMicrobenchmark { + + public enum ByteBufType { + SMALL_CHUNKS { + @Override + ByteBuf newBuffer(int length) { + return newBufferSmallChunks(length); + } + }, + LARGE_CHUNKS { + @Override + ByteBuf newBuffer(int length) { + return newBufferLargeChunks(length); + } + }; + abstract ByteBuf newBuffer(int length); + } + + @Param({ "8", "64", "1024", "10240", "102400", "1024000" }) + public int size; + + @Param + public ByteBufType bufferType; + + private ByteBuf buffer; + + @Setup + public void setup() { + buffer = bufferType.newBuffer(size); + } + + @TearDown + public void teardown() { + buffer.release(); + } + + private static final ByteProcessor TEST_PROCESSOR = new ByteProcessor() { + @Override + public boolean process(byte value) throws Exception { + return value == 'b'; // false + } + }; + + @Benchmark + public int forEachByte() { + buffer.setIndex(0, buffer.capacity()); + buffer.forEachByte(TEST_PROCESSOR); + return buffer.forEachByteDesc(TEST_PROCESSOR); + } + + @Benchmark + public int sequentialWriteAndRead() { + buffer.clear(); + for (int i = 0, l = buffer.writableBytes(); i < l; i++) { + buffer.writeByte('a'); + } + for (int i = 0, l = buffer.readableBytes(); i < l; i++) { + if (buffer.readByte() == 'b') { + return -1; + } + } + return 1; + } + + private static ByteBuf newBufferSmallChunks(int length) { + + List buffers = new ArrayList(((length + 1) / 45) * 19); + for (int i = 0; i < length + 45; i += 45) { + for (int j = 1; j <= 9; j++) { + buffers.add(EMPTY_BUFFER); + buffers.add(wrappedBuffer(new byte[j])); + } + buffers.add(EMPTY_BUFFER); + } + + ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[0])); + + // Truncate to the requested capacity. + return buffer.capacity(length).writerIndex(0); + } + + private static ByteBuf newBufferLargeChunks(int length) { + + List buffers = new ArrayList((length + 1) / 512); + for (int i = 0; i < length + 1536; i += 1536) { + buffers.add(wrappedBuffer(new byte[512])); + buffers.add(EMPTY_BUFFER); + buffers.add(wrappedBuffer(new byte[1024])); + } + + ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[0])); + + // Truncate to the requested capacity. + return buffer.capacity(length).writerIndex(0); + } +} diff --git a/microbench/src/main/java/io/netty/buffer/CompositeByteBufWriteOutBenchmark.java b/microbench/src/main/java/io/netty/buffer/CompositeByteBufWriteOutBenchmark.java new file mode 100644 index 000000000000..e525c8cd0aa5 --- /dev/null +++ b/microbench/src/main/java/io/netty/buffer/CompositeByteBufWriteOutBenchmark.java @@ -0,0 +1,114 @@ +/* + * Copyright 2018 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: + * + * http://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.buffer; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import io.netty.microbench.util.AbstractMicrobenchmark; + +import static io.netty.buffer.Unpooled.wrappedBuffer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 12, time = 1, timeUnit = TimeUnit.SECONDS) +public class CompositeByteBufWriteOutBenchmark extends AbstractMicrobenchmark { + + public enum ByteBufType { + SMALL_CHUNKS { + @Override + ByteBuf[] sourceBuffers(int length) { + return makeSmallChunks(length); + } + }, + LARGE_CHUNKS { + @Override + ByteBuf[] sourceBuffers(int length) { + return makeLargeChunks(length); + } + }; + abstract ByteBuf[] sourceBuffers(int length); + } + + @Override + protected String[] jvmArgs() { + // Ensure we minimize the GC overhead by sizing the heap big enough. + return new String[] { "-XX:MaxDirectMemorySize=2g", "-Xmx4g", "-Xms4g", "-Xmn3g" }; + } + + @Param({ "64", "1024", "10240", "102400", "1024000" }) + public int size; + + @Param + public ByteBufType bufferType; + + private ByteBuf targetBuffer; + + private ByteBuf[] sourceBufs; + + @Setup + public void setup() { + targetBuffer = PooledByteBufAllocator.DEFAULT.directBuffer(size + 2048); + sourceBufs = bufferType.sourceBuffers(size); + } + + @TearDown + public void teardown() { + targetBuffer.release(); + } + + @Benchmark + public int writeCBB() { + ByteBuf cbb = Unpooled.wrappedBuffer(Integer.MAX_VALUE, sourceBufs); // CompositeByteBuf + return targetBuffer.clear().writeBytes(cbb).readableBytes(); + } + + @Benchmark + public int writeFCBB() { + ByteBuf cbb = Unpooled.wrappedUnmodifiableBuffer(sourceBufs); // FastCompositeByteBuf + return targetBuffer.clear().writeBytes(cbb).readableBytes(); + } + + private static ByteBuf[] makeSmallChunks(int length) { + + List buffers = new ArrayList(((length + 1) / 48) * 9); + for (int i = 0; i < length + 48; i += 48) { + for (int j = 4; j <= 12; j++) { + buffers.add(wrappedBuffer(new byte[j])); + } + } + + return buffers.toArray(new ByteBuf[0]); + } + + private static ByteBuf[] makeLargeChunks(int length) { + + List buffers = new ArrayList((length + 1) / 768); + for (int i = 0; i < length + 1536; i += 1536) { + buffers.add(wrappedBuffer(new byte[512])); + buffers.add(wrappedBuffer(new byte[1024])); + } + + return buffers.toArray(new ByteBuf[0]); + } +} From 5954110b9a3aef1ca06de96cb74d76e2fc335c6c Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Mon, 5 Nov 2018 12:11:28 -0800 Subject: [PATCH 251/417] Use ByteBufUtil.BYTE_ARRAYS ThreadLocal temporary arrays in more places (#8464) Motivation: #8388 introduced a reusable ThreadLocal for use in decodeString(...). It can be used in more places in the buffer package to avoid temporary allocations of small arrays. Modifications: Encapsulate use of the ThreadLocal in a static package-private ByteBufUtil.threadLocalTempArray(int) method, and make use of it from a handful of new places including ByteBufUtil.readBytes(...). Result: Fewer short-lived small byte array allocations. --- .../java/io/netty/buffer/ByteBufUtil.java | 25 +++++++++++-------- .../io/netty/buffer/PooledDirectByteBuf.java | 5 ++-- .../netty/buffer/ReadOnlyByteBufferBuf.java | 7 +++--- .../netty/buffer/UnpooledDirectByteBuf.java | 4 +-- .../io/netty/buffer/UnsafeByteBufUtil.java | 7 +++--- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index 500e5e9c67e8..86b0e2f2d703 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -56,7 +56,7 @@ public final class ByteBufUtil { private static final FastThreadLocal BYTE_ARRAYS = new FastThreadLocal() { @Override protected byte[] initialValue() throws Exception { - return PlatformDependent.allocateUninitializedArray(1024); + return PlatformDependent.allocateUninitializedArray(MAX_TL_ARRAY_LEN); } }; @@ -95,6 +95,16 @@ protected byte[] initialValue() throws Exception { logger.debug("-Dio.netty.maxThreadLocalCharBufferSize: {}", MAX_CHAR_BUFFER_SIZE); } + static final int MAX_TL_ARRAY_LEN = 1024; + + /** + * Allocates a new array if minLength > {@link ByteBufUtil#MAX_TL_ARRAY_LEN} + */ + static byte[] threadLocalTempArray(int minLength) { + return minLength <= MAX_TL_ARRAY_LEN ? BYTE_ARRAYS.get() + : PlatformDependent.allocateUninitializedArray(minLength); + } + /** * Returns a hex dump * of the specified buffer's readable bytes. @@ -768,11 +778,7 @@ static String decodeString(ByteBuf src, int readerIndex, int len, Charset charse array = src.array(); offset = src.arrayOffset() + readerIndex; } else { - if (len <= 1024) { - array = BYTE_ARRAYS.get(); - } else { - array = PlatformDependent.allocateUninitializedArray(len); - } + array = threadLocalTempArray(len); offset = 0; src.getBytes(readerIndex, array, 0, len); } @@ -1392,7 +1398,9 @@ static void readBytes(ByteBufAllocator allocator, ByteBuffer buffer, int positio int chunkLen = Math.min(length, WRITE_CHUNK_SIZE); buffer.clear().position(position); - if (allocator.isDirectBufferPooled()) { + if (length <= MAX_TL_ARRAY_LEN || !allocator.isDirectBufferPooled()) { + getBytes(buffer, threadLocalTempArray(length), 0, chunkLen, out, length); + } else { // if direct buffers are pooled chances are good that heap buffers are pooled as well. ByteBuf tmpBuf = allocator.heapBuffer(chunkLen); try { @@ -1402,9 +1410,6 @@ static void readBytes(ByteBufAllocator allocator, ByteBuffer buffer, int positio } finally { tmpBuf.release(); } - } else { - byte[] tmp = PlatformDependent.allocateUninitializedArray(length); - getBytes(buffer, tmp, 0, chunkLen, out, length); } } } diff --git a/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java index 8b326ccef744..9601150b319d 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java @@ -17,7 +17,6 @@ package io.netty.buffer; import io.netty.util.Recycler; -import io.netty.util.internal.PlatformDependent; import java.io.IOException; import java.io.InputStream; @@ -352,8 +351,8 @@ public ByteBuf setBytes(int index, ByteBuffer src) { @Override public int setBytes(int index, InputStream in, int length) throws IOException { checkIndex(index, length); - byte[] tmp = PlatformDependent.allocateUninitializedArray(length); - int readBytes = in.read(tmp); + byte[] tmp = ByteBufUtil.threadLocalTempArray(length); + int readBytes = in.read(tmp, 0, length); if (readBytes <= 0) { return readBytes; } diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java index ec34930ee11b..c7cda05fd979 100644 --- a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java @@ -15,7 +15,6 @@ */ package io.netty.buffer; -import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import java.io.IOException; @@ -356,11 +355,11 @@ public ByteBuf getBytes(int index, OutputStream out, int length) throws IOExcept if (buffer.hasArray()) { out.write(buffer.array(), index + buffer.arrayOffset(), length); } else { - byte[] tmp = PlatformDependent.allocateUninitializedArray(length); + byte[] tmp = ByteBufUtil.threadLocalTempArray(length); ByteBuffer tmpBuf = internalNioBuffer(); tmpBuf.clear().position(index); - tmpBuf.get(tmp); - out.write(tmp); + tmpBuf.get(tmp, 0, length); + out.write(tmp, 0, length); } return this; } diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java index 71ab3df86a3f..60167cf341f8 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java @@ -561,8 +561,8 @@ public int setBytes(int index, InputStream in, int length) throws IOException { if (buffer.hasArray()) { return in.read(buffer.array(), buffer.arrayOffset() + index, length); } else { - byte[] tmp = PlatformDependent.allocateUninitializedArray(length); - int readBytes = in.read(tmp); + byte[] tmp = ByteBufUtil.threadLocalTempArray(length); + int readBytes = in.read(tmp, 0, length); if (readBytes <= 0) { return readBytes; } diff --git a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java index ead07253bdaf..a2a1f5f20165 100644 --- a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java @@ -584,7 +584,9 @@ static void getBytes(AbstractByteBuf buf, long addr, int index, OutputStream out buf.checkIndex(index, length); if (length != 0) { int len = Math.min(length, ByteBufUtil.WRITE_CHUNK_SIZE); - if (buf.alloc().isDirectBufferPooled()) { + if (len <= ByteBufUtil.MAX_TL_ARRAY_LEN || !buf.alloc().isDirectBufferPooled()) { + getBytes(addr, ByteBufUtil.threadLocalTempArray(length), 0, len, out, length); + } else { // if direct buffers are pooled chances are good that heap buffers are pooled as well. ByteBuf tmpBuf = buf.alloc().heapBuffer(len); try { @@ -594,9 +596,6 @@ static void getBytes(AbstractByteBuf buf, long addr, int index, OutputStream out } finally { tmpBuf.release(); } - } else { - byte[] tmp = PlatformDependent.allocateUninitializedArray(len); - getBytes(addr, tmp, 0, len, out, length); } } } From 28f9136824499d7bca318f4496339d82fb42c46a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=B6=E6=97=A0=E4=B8=A4=E4=B8=B6?= <442367943@qq.com> Date: Tue, 6 Nov 2018 18:21:56 +0800 Subject: [PATCH 252/417] Replace ConcurrentHashMap at allLeaks with a thread-safe set (#8467) Motivation: allLeaks is to store the DefaultResourceLeak. When we actually use it, the key is DefaultResourceLeak, and the value is actually a meaningless value. We only care about the keys of allLeaks and don't care about the values. So Set is more in line with this scenario. Using Set as a container is more consistent with the definition of a container than Map. Modification: Replace allLeaks with set. Create a thread-safe set using 'Collections.newSetFromMap(new ConcurrentHashMap, Boolean>()).' --- .../io/netty/util/ResourceLeakDetector.java | 34 +++++-------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetector.java b/common/src/main/java/io/netty/util/ResourceLeakDetector.java index 5f26926cf7a0..a62f6df05757 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetector.java +++ b/common/src/main/java/io/netty/util/ResourceLeakDetector.java @@ -26,8 +26,10 @@ import java.lang.ref.ReferenceQueue; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReference; @@ -159,7 +161,8 @@ public static Level getLevel() { } /** the collection of active resources */ - private final ConcurrentMap, LeakEntry> allLeaks = PlatformDependent.newConcurrentHashMap(); + private final Set> allLeaks = + Collections.newSetFromMap(new ConcurrentHashMap, Boolean>()); private final ReferenceQueue refQueue = new ReferenceQueue(); private final ConcurrentMap reportedLeaks = PlatformDependent.newConcurrentHashMap(); @@ -353,13 +356,13 @@ private static final class DefaultResourceLeak @SuppressWarnings("unused") private volatile int droppedRecords; - private final ConcurrentMap, LeakEntry> allLeaks; + private final Set> allLeaks; private final int trackedHash; DefaultResourceLeak( Object referent, ReferenceQueue refQueue, - ConcurrentMap, LeakEntry> allLeaks) { + Set> allLeaks) { super(referent, refQueue); assert referent != null; @@ -368,7 +371,7 @@ private static final class DefaultResourceLeak // It's important that we not store a reference to the referent as this would disallow it from // be collected via the WeakReference. trackedHash = System.identityHashCode(referent); - allLeaks.put(this, LeakEntry.INSTANCE); + allLeaks.add(this); // Create a new Record so we always have the creation stacktrace included. headUpdater.set(this, new Record(Record.BOTTOM)); this.allLeaks = allLeaks; @@ -441,13 +444,12 @@ private void record0(Object hint) { boolean dispose() { clear(); - return allLeaks.remove(this, LeakEntry.INSTANCE); + return allLeaks.remove(this); } @Override public boolean close() { - // Use the ConcurrentMap remove method, which avoids allocating an iterator. - if (allLeaks.remove(this, LeakEntry.INSTANCE)) { + if (allLeaks.remove(this)) { // Call clear so the reference is not even enqueued. clear(); headUpdater.set(this, null); @@ -636,22 +638,4 @@ public String toString() { return buf.toString(); } } - - private static final class LeakEntry { - static final LeakEntry INSTANCE = new LeakEntry(); - private static final int HASH = System.identityHashCode(INSTANCE); - - private LeakEntry() { - } - - @Override - public int hashCode() { - return HASH; - } - - @Override - public boolean equals(Object obj) { - return obj == this; - } - } } From fd57d971d12f0fdf74b39f3eba25164a8e580f6b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 7 Nov 2018 12:16:04 +0100 Subject: [PATCH 253/417] Override and so delegate all methods in OpenSslX509Certificate (#8472) Motivation: We did not override all methods in OpenSslX509Certificate and delegate to the internal 509Certificate. Modifications: Add missing overrides. Result: More correct implementation --- .../handler/ssl/OpenSslX509Certificate.java | 36 +++++++++++++++++++ pom.xml | 1 + 2 files changed, 37 insertions(+) diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509Certificate.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509Certificate.java index 77d0713613a9..b94a19577be2 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509Certificate.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509Certificate.java @@ -15,20 +15,25 @@ */ package io.netty.handler.ssl; +import javax.security.auth.x500.X500Principal; import java.io.ByteArrayInputStream; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Principal; +import java.security.Provider; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; +import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; +import java.util.Collection; import java.util.Date; +import java.util.List; import java.util.Set; final class OpenSslX509Certificate extends X509Certificate { @@ -50,6 +55,37 @@ public void checkValidity(Date date) throws CertificateExpiredException, Certifi unwrap().checkValidity(date); } + @Override + public X500Principal getIssuerX500Principal() { + return unwrap().getIssuerX500Principal(); + } + + @Override + public X500Principal getSubjectX500Principal() { + return unwrap().getSubjectX500Principal(); + } + + @Override + public List getExtendedKeyUsage() throws CertificateParsingException { + return unwrap().getExtendedKeyUsage(); + } + + @Override + public Collection> getSubjectAlternativeNames() throws CertificateParsingException { + return unwrap().getSubjectAlternativeNames(); + } + + @Override + public Collection> getIssuerAlternativeNames() throws CertificateParsingException { + return unwrap().getSubjectAlternativeNames(); + } + + // No @Override annotation as it was only introduced in Java8. + public void verify(PublicKey key, Provider sigProvider) + throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { + unwrap().verify(key, sigProvider); + } + @Override public int getVersion() { return unwrap().getVersion(); diff --git a/pom.xml b/pom.xml index bde6a377ad02..59e442502c37 100644 --- a/pom.xml +++ b/pom.xml @@ -777,6 +777,7 @@ java.util.concurrent.atomic.LongAdder java.util.function.BiFunction + java.security.cert.X509Certificate java.net.InetAddress From 8a24df88a4ff5519176767aee01bf6186015d471 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 8 Nov 2018 15:22:33 +0100 Subject: [PATCH 254/417] Ensure we correctly call wrapEngine(...) during tests. (#8473) Motivation: We should call wrapEngine(...) in our SSLEngineTest to correctly detect all errors in case of the OpenSSLEngine. Modifications: Add missing wrapEngine(...) calls. Result: More correct tests --- .../src/test/java/io/netty/handler/ssl/SSLEngineTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index ba2cdf2c2248..4de0bd8d0fb8 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -969,7 +969,7 @@ protected void initChannel(Channel ch) { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ChannelPipeline p = ch.pipeline(); - SSLEngine engine = serverSslCtx.newEngine(ch.alloc()); + SSLEngine engine = wrapEngine(serverSslCtx.newEngine(ch.alloc())); engine.setUseClientMode(false); engine.setNeedClientAuth(true); @@ -1679,7 +1679,7 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc @Override protected void initChannel(Channel ch) throws Exception { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); - ch.pipeline().addLast(new SslHandler(clientSslCtx.newEngine(ch.alloc()))); + ch.pipeline().addLast(new SslHandler(wrapEngine(clientSslCtx.newEngine(ch.alloc())))); } }).connect(serverChannel.localAddress()).syncUninterruptibly().channel(); @@ -2649,7 +2649,7 @@ public void testInvalidCipher() throws Exception { try { serverSslCtx = SslContextBuilder.forServer(cert.key(), cert.cert()).sslProvider(sslClientProvider()) .ciphers(cipherList).build(); - server = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); + server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); fail(); } catch (IllegalArgumentException expected) { // expected when invalid cipher is used. From e766469e87e52cf6e1af0122d4403d6a8fddc781 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 9 Nov 2018 16:50:58 +0100 Subject: [PATCH 255/417] Update to openjdk 12ea19 (#8487) Motivation: We should test against latest EA releases. Modifications: Update to openkdk 12ea19 Result: Use latest openjdk 12 EA build on the CI. --- docker/docker-compose.centos-6.112.yaml | 2 +- docker/docker-compose.centos-7.112.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.112.yaml b/docker/docker-compose.centos-6.112.yaml index a57ce0c94f47..ded410bea7dc 100644 --- a/docker/docker-compose.centos-6.112.yaml +++ b/docker/docker-compose.centos-6.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "openjdk@1.12.0-17" + java_version : "openjdk@1.12.0-19" test: image: netty:centos-6-1.12 diff --git a/docker/docker-compose.centos-7.112.yaml b/docker/docker-compose.centos-7.112.yaml index 5763acb2b4ac..9eb4fb1da720 100644 --- a/docker/docker-compose.centos-7.112.yaml +++ b/docker/docker-compose.centos-7.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "openjdk@1.12.0-17" + java_version : "openjdk@1.12.0-19" test: image: netty:centos-7-1.12 From a140e6dcad0c95b8867f4c6ff1e14537d5fd6cab Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Fri, 9 Nov 2018 10:23:53 -0700 Subject: [PATCH 256/417] Make Http2StreamFrameToHttpObjectCodec truly @Sharable (#8482) Motivation: The `Http2StreamFrameToHttpObjectCodec` is marked `@Sharable` but mutates an internal `HttpScheme` field every time it is added to a pipeline. Modifications: Instead of storing the `HttpScheme` in the handler we store it as an attribute on the parent channel. Result: Fixes #8480. --- .../Http2StreamFrameToHttpObjectCodec.java | 43 +++++++++---- ...Http2StreamFrameToHttpObjectCodecTest.java | 62 +++++++++++++++++++ 2 files changed, 94 insertions(+), 11 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec.java index 36a7fba1c0c6..e13a45fe0152 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec.java @@ -41,6 +41,8 @@ import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.ssl.SslHandler; +import io.netty.util.Attribute; +import io.netty.util.AttributeKey; import io.netty.util.internal.UnstableApi; import java.util.List; @@ -57,16 +59,17 @@ @UnstableApi @Sharable public class Http2StreamFrameToHttpObjectCodec extends MessageToMessageCodec { + + private static final AttributeKey SCHEME_ATTR_KEY = + AttributeKey.valueOf(HttpScheme.class, "STREAMFRAMECODEC_SCHEME"); + private final boolean isServer; private final boolean validateHeaders; - private HttpScheme scheme; - public Http2StreamFrameToHttpObjectCodec(final boolean isServer, final boolean validateHeaders) { this.isServer = isServer; this.validateHeaders = validateHeaders; - scheme = HttpScheme.HTTP; } public Http2StreamFrameToHttpObjectCodec(final boolean isServer) { @@ -154,7 +157,7 @@ protected void encode(ChannelHandlerContext ctx, HttpObject obj, List ou final HttpResponse res = (HttpResponse) obj; if (res.status().equals(HttpResponseStatus.CONTINUE)) { if (res instanceof FullHttpResponse) { - final Http2Headers headers = toHttp2Headers(res); + final Http2Headers headers = toHttp2Headers(ctx, res); out.add(new DefaultHttp2HeadersFrame(headers, false)); return; } else { @@ -165,7 +168,7 @@ protected void encode(ChannelHandlerContext ctx, HttpObject obj, List ou } if (obj instanceof HttpMessage) { - Http2Headers headers = toHttp2Headers((HttpMessage) obj); + Http2Headers headers = toHttp2Headers(ctx, (HttpMessage) obj); boolean noMoreFrames = false; if (obj instanceof FullHttpMessage) { FullHttpMessage full = (FullHttpMessage) obj; @@ -184,11 +187,11 @@ protected void encode(ChannelHandlerContext ctx, HttpObject obj, List ou } } - private Http2Headers toHttp2Headers(final HttpMessage msg) { + private Http2Headers toHttp2Headers(final ChannelHandlerContext ctx, final HttpMessage msg) { if (msg instanceof HttpRequest) { msg.headers().set( HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), - scheme.name()); + connectionScheme(ctx)); } return HttpConversionUtil.toHttp2Headers(msg, validateHeaders); @@ -213,17 +216,35 @@ private FullHttpMessage newFullMessage(final int id, public void handlerAdded(final ChannelHandlerContext ctx) throws Exception { super.handlerAdded(ctx); - // this handler is typically used on an Http2StreamChannel. at this + // this handler is typically used on an Http2StreamChannel. At this // stage, ssl handshake should've been established. checking for the // presence of SslHandler in the parent's channel pipeline to // determine the HTTP scheme should suffice, even for the case where // SniHandler is used. - scheme = isSsl(ctx) ? HttpScheme.HTTPS : HttpScheme.HTTP; + final Attribute schemeAttribute = connectionSchemeAttribute(ctx); + if (schemeAttribute.get() == null) { + final HttpScheme scheme = isSsl(ctx) ? HttpScheme.HTTPS : HttpScheme.HTTP; + schemeAttribute.set(scheme); + } } protected boolean isSsl(final ChannelHandlerContext ctx) { - final Channel ch = ctx.channel(); - final Channel connChannel = (ch instanceof Http2StreamChannel) ? ch.parent() : ch; + final Channel connChannel = connectionChannel(ctx); return null != connChannel.pipeline().get(SslHandler.class); } + + private static HttpScheme connectionScheme(ChannelHandlerContext ctx) { + final HttpScheme scheme = connectionSchemeAttribute(ctx).get(); + return scheme == null ? HttpScheme.HTTP : scheme; + } + + private static Attribute connectionSchemeAttribute(ChannelHandlerContext ctx) { + final Channel ch = connectionChannel(ctx); + return ch.attr(SCHEME_ATTR_KEY); + } + + private static Channel connectionChannel(ChannelHandlerContext ctx) { + final Channel ch = ctx.channel(); + return ch instanceof Http2StreamChannel ? ch.parent() : ch; + } } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodecTest.java index 45e781c86c47..393a4060ef4f 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodecTest.java @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; @@ -871,4 +872,65 @@ public void testPassThroughOtherAsClient() throws Exception { frame.release(); } } + + @Test + public void testIsSharableBetweenChannels() throws Exception { + final Queue frames = new ConcurrentLinkedQueue(); + final ChannelHandler sharedHandler = new Http2StreamFrameToHttpObjectCodec(false); + + final SslContext ctx = SslContextBuilder.forClient().sslProvider(SslProvider.JDK).build(); + EmbeddedChannel tlsCh = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT), + new ChannelOutboundHandlerAdapter() { + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { + if (msg instanceof Http2StreamFrame) { + frames.add((Http2StreamFrame) msg); + promise.setSuccess(); + } else { + ctx.write(msg, promise); + } + } + }, sharedHandler); + + EmbeddedChannel plaintextCh = new EmbeddedChannel( + new ChannelOutboundHandlerAdapter() { + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { + if (msg instanceof Http2StreamFrame) { + frames.add((Http2StreamFrame) msg); + promise.setSuccess(); + } else { + ctx.write(msg, promise); + } + } + }, sharedHandler); + + FullHttpRequest req = new DefaultFullHttpRequest( + HttpVersion.HTTP_1_1, HttpMethod.GET, "/hello/world"); + assertTrue(tlsCh.writeOutbound(req)); + assertTrue(tlsCh.finishAndReleaseAll()); + + Http2HeadersFrame headersFrame = (Http2HeadersFrame) frames.poll(); + Http2Headers headers = headersFrame.headers(); + + assertThat(headers.scheme().toString(), is("https")); + assertThat(headers.method().toString(), is("GET")); + assertThat(headers.path().toString(), is("/hello/world")); + assertTrue(headersFrame.isEndStream()); + assertNull(frames.poll()); + + // Run the plaintext channel + req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/hello/world"); + assertFalse(plaintextCh.writeOutbound(req)); + assertFalse(plaintextCh.finishAndReleaseAll()); + + headersFrame = (Http2HeadersFrame) frames.poll(); + headers = headersFrame.headers(); + + assertThat(headers.scheme().toString(), is("http")); + assertThat(headers.method().toString(), is("GET")); + assertThat(headers.path().toString(), is("/hello/world")); + assertTrue(headersFrame.isEndStream()); + assertNull(frames.poll()); + } } From 0f8ce1b2847b61efa39b8f41807dc64b0c672314 Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Fri, 9 Nov 2018 09:24:38 -0800 Subject: [PATCH 257/417] Fix incorrect sizing of temp byte arrays in (Unsafe)ByteBufUtil (#8484) Motivation: Two similar bugs were introduced by myself in separate recent PRs #8393 and #8464, while optimizing the assignment/handling of temporary arrays in ByteBufUtil and UnsafeByteBufUtil. The temp arrays allocated for buffering data written to an OutputStream are incorrectly sized to the full length of the data to copy rather than being capped at WRITE_CHUNK_SIZE. Unfortunately one of these is in the 4.1.31.Final release, I'm really sorry and will be more careful in future. This kind of thing is tricky to cover in unit tests. Modifications: Revert the temp array allocations back to their original sizes. Avoid making duplicate calls to ByteBuf.capacity() in a couple of places in ByteBufUtil (unrelated thing I noticed, can remove it from this PR if desired!) Result: Temporary byte arrays will be reverted to their originally intended sizes. --- .../src/main/java/io/netty/buffer/ByteBufUtil.java | 14 ++++++++------ .../java/io/netty/buffer/UnsafeByteBufUtil.java | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index 86b0e2f2d703..6cd9b211ebf1 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -462,8 +462,9 @@ private static int firstIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte } private static int lastIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) { - fromIndex = Math.min(fromIndex, buffer.capacity()); - if (fromIndex < 0 || buffer.capacity() == 0) { + int capacity = buffer.capacity(); + fromIndex = Math.min(fromIndex, capacity); + if (fromIndex < 0 || capacity == 0) { return -1; } @@ -829,13 +830,14 @@ public static byte[] getBytes(ByteBuf buf, int start, int length) { * If {@code copy} is false the underlying storage will be shared, if possible. */ public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy) { - if (isOutOfBounds(start, length, buf.capacity())) { + int capacity = buf.capacity(); + if (isOutOfBounds(start, length, capacity)) { throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length - + ") <= " + "buf.capacity(" + buf.capacity() + ')'); + + ") <= " + "buf.capacity(" + capacity + ')'); } if (buf.hasArray()) { - if (copy || start != 0 || length != buf.capacity()) { + if (copy || start != 0 || length != capacity) { int baseOffset = buf.arrayOffset() + start; return Arrays.copyOfRange(buf.array(), baseOffset, baseOffset + length); } else { @@ -1399,7 +1401,7 @@ static void readBytes(ByteBufAllocator allocator, ByteBuffer buffer, int positio buffer.clear().position(position); if (length <= MAX_TL_ARRAY_LEN || !allocator.isDirectBufferPooled()) { - getBytes(buffer, threadLocalTempArray(length), 0, chunkLen, out, length); + getBytes(buffer, threadLocalTempArray(chunkLen), 0, chunkLen, out, length); } else { // if direct buffers are pooled chances are good that heap buffers are pooled as well. ByteBuf tmpBuf = allocator.heapBuffer(chunkLen); diff --git a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java index a2a1f5f20165..05918a8ebb18 100644 --- a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java @@ -585,7 +585,7 @@ static void getBytes(AbstractByteBuf buf, long addr, int index, OutputStream out if (length != 0) { int len = Math.min(length, ByteBufUtil.WRITE_CHUNK_SIZE); if (len <= ByteBufUtil.MAX_TL_ARRAY_LEN || !buf.alloc().isDirectBufferPooled()) { - getBytes(addr, ByteBufUtil.threadLocalTempArray(length), 0, len, out, length); + getBytes(addr, ByteBufUtil.threadLocalTempArray(len), 0, len, out, length); } else { // if direct buffers are pooled chances are good that heap buffers are pooled as well. ByteBuf tmpBuf = buf.alloc().heapBuffer(len); From 35471a1a6fa64ac61829afa9f19634e11d537a8a Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 11 Nov 2018 07:18:21 +0100 Subject: [PATCH 258/417] Enable netty-tcnative shading test again (#8492) Motivation: We disabled the test at some point but it should work now without any problems. Modifications: Remove @Ignore from test. Result: Verify shading of netty-tcnative on CI. --- .../src/test/java/io/netty/testsuite/shading/ShadingIT.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java index 0f45fddef84e..93edde6cdd0f 100644 --- a/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java +++ b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java @@ -16,7 +16,6 @@ package io.netty.testsuite.shading; import io.netty.util.internal.PlatformDependent; -import org.junit.Ignore; import org.junit.Test; import java.lang.reflect.Method; @@ -34,7 +33,6 @@ public void testShadingNativeTransport() throws Exception { testShading0(SHADING_PREFIX2, className); } - @Ignore("Figure out why this sometimes fail on the CI") @Test public void testShadingTcnative() throws Exception { String className = "io.netty.handler.ssl.OpenSsl"; From c0dfb568a2e13feb0872243e6d1a57c5dab15fd5 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 11 Nov 2018 07:23:08 +0100 Subject: [PATCH 259/417] =?UTF-8?q?SSLHandler=20may=20throw=20AssertionErr?= =?UTF-8?q?or=20if=20writes=20occur=20before=20channelAct=E2=80=A6=20(#848?= =?UTF-8?q?6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: If you attempt to write to a channel with an SslHandler prior to channelActive being called you can hit an assertion. In particular - if you write to a channel it forces some handshaking (through flush calls) to occur. The AssertionError only happens on Java11+. Modifications: - Replace assert by an "early return" in case of the handshake be done already. - Add unit test that verifies we do not hit the AssertionError anymore and that the future is correctly failed. Result: Fixes https://github.com/netty/netty/issues/8479. --- .../java/io/netty/handler/ssl/SslHandler.java | 9 ++- .../io/netty/handler/ssl/SslHandlerTest.java | 75 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index f1de1360bfea..0e3db8d83a84 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -1745,9 +1745,16 @@ public void operationComplete(Future future) throws Exception { // is in progress. See https://github.com/netty/netty/issues/4718. return; } else { + if (handshakePromise.isDone()) { + // If the handshake is done already lets just return directly as there is no need to trigger it again. + // This can happen if the handshake(...) was triggered before we called channelActive(...) by a + // flush() that was triggered by a ChannelFutureListener that was added to the ChannelFuture returned + // from the connect(...) method. In this case we will see the flush() happen before we had a chance to + // call fireChannelActive() on the pipeline. + return; + } // Forced to reuse the old handshake. p = handshakePromise; - assert !p.isDone(); } // Begin handshake. diff --git a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java index 75375008f78a..ab7a63c908cd 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java @@ -54,6 +54,7 @@ import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.Promise; +import org.hamcrest.CoreMatchers; import org.junit.Test; import java.net.InetSocketAddress; @@ -63,6 +64,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -679,4 +681,77 @@ public void testOutboundClosedAfterChannelInactive() throws Exception { assertTrue(engine.isOutboundDone()); } + + @Test(timeout = 10000) + public void testHandshakeFailedByWriteBeforeChannelActive() throws Exception { + final SslContext sslClientCtx = SslContextBuilder.forClient() + .protocols(SslUtils.PROTOCOL_SSL_V3) + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(SslProvider.JDK).build(); + + EventLoopGroup group = new NioEventLoopGroup(); + Channel sc = null; + Channel cc = null; + final CountDownLatch activeLatch = new CountDownLatch(1); + final AtomicReference errorRef = new AtomicReference(); + final SslHandler sslHandler = sslClientCtx.newHandler(UnpooledByteBufAllocator.DEFAULT); + try { + sc = new ServerBootstrap() + .group(group) + .channel(NioServerSocketChannel.class) + .childHandler(new ChannelInboundHandlerAdapter()) + .bind(new InetSocketAddress(0)).syncUninterruptibly().channel(); + + cc = new Bootstrap() + .group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) throws Exception { + ch.pipeline().addLast(sslHandler); + ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) + throws Exception { + if (cause instanceof AssertionError) { + errorRef.set((AssertionError) cause); + } + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + activeLatch.countDown(); + } + }); + } + }).connect(sc.localAddress()).addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + // Write something to trigger the handshake before fireChannelActive is called. + future.channel().writeAndFlush(wrappedBuffer(new byte [] { 1, 2, 3, 4 })); + } + }).syncUninterruptibly().channel(); + + // Ensure there is no AssertionError thrown by having the handshake failed by the writeAndFlush(...) before + // channelActive(...) was called. Let's first wait for the activeLatch countdown to happen and after this + // check if we saw and AssertionError (even if we timed out waiting). + activeLatch.await(5, TimeUnit.SECONDS); + AssertionError error = errorRef.get(); + if (error != null) { + throw error; + } + assertThat(sslHandler.handshakeFuture().await().cause(), + CoreMatchers.instanceOf(SSLException.class)); + } finally { + if (cc != null) { + cc.close().syncUninterruptibly(); + } + if (sc != null) { + sc.close().syncUninterruptibly(); + } + group.shutdownGracefully(); + + ReferenceCountUtil.release(sslClientCtx); + } + } } From 88e4817cefb4b4d092e4f6a12f0599c54646db18 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 12 Nov 2018 20:59:44 +0100 Subject: [PATCH 260/417] Include correct dependencies for testsuite-shading on windows. (#8491) Motivation: We missed to include a profile for windows which means that we did not have the correct dependencies setup. Modifications: - Add missing profile - Add assumeFalse(...) to ensure we do only test the native transpot shading on non windows platforms. - Explicit specify dependency on netty-common Result: Fixes https://github.com/netty/netty/issues/8489. --- testsuite-shading/pom.xml | 41 +++++++++++++++++++ .../io/netty/testsuite/shading/ShadingIT.java | 7 ++++ 2 files changed, 48 insertions(+) diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index 9e53b49513d5..17a8a94b2a1c 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -65,6 +65,35 @@ + + windows + + + windows + + + + + ${project.groupId} + netty-common + ${project.version} + compile + + + ${project.groupId} + netty-handler + ${project.version} + compile + + + ${project.groupId} + ${tcnative.artifactId} + ${tcnative.version} + ${tcnative.classifier} + compile + + + mac @@ -76,6 +105,12 @@ netty_transport_native_kqueue_${os.detected.arch}.jnilib + + ${project.groupId} + netty-common + ${project.version} + compile + ${project.groupId} netty-transport-native-kqueue @@ -214,6 +249,12 @@ netty_transport_native_epoll_${os.detected.arch}.so + + ${project.groupId} + netty-common + ${project.version} + compile + ${project.groupId} netty-transport-native-epoll diff --git a/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java index 93edde6cdd0f..51cef5f56836 100644 --- a/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java +++ b/testsuite-shading/src/test/java/io/netty/testsuite/shading/ShadingIT.java @@ -17,6 +17,7 @@ import io.netty.util.internal.PlatformDependent; import org.junit.Test; +import org.junit.Assume; import java.lang.reflect.Method; @@ -27,6 +28,9 @@ public class ShadingIT { @Test public void testShadingNativeTransport() throws Exception { + // Skip on windows. + Assume.assumeFalse(PlatformDependent.isWindows()); + String className = PlatformDependent.isOsx() ? "io.netty.channel.kqueue.KQueue" : "io.netty.channel.epoll.Epoll"; testShading0(SHADING_PREFIX, className); @@ -35,6 +39,9 @@ public void testShadingNativeTransport() throws Exception { @Test public void testShadingTcnative() throws Exception { + // Skip on windows. + Assume.assumeFalse(PlatformDependent.isWindows()); + String className = "io.netty.handler.ssl.OpenSsl"; testShading0(SHADING_PREFIX, className); testShading0(SHADING_PREFIX2, className); From 4c73d24ea8f1bc4d51d2cd85580edbb48aaef5bb Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 13 Nov 2018 19:22:38 +0100 Subject: [PATCH 261/417] Handshake timeout may never be scheduled if handshake starts via a flush or starttls is used. (#8494) Motivation: We did not correctly schedule the handshake timeout if the handshake was either started by a flush(...) or if starttls was used. Modifications: - Correctly setup timeout in all cases - Add unit tests. Result: Fixes https://github.com/netty/netty/issues/8493. --- .../java/io/netty/handler/ssl/SslHandler.java | 32 +++++--- .../io/netty/handler/ssl/SslHandlerTest.java | 76 ++++++++++++++++++- 2 files changed, 94 insertions(+), 14 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index 0e3db8d83a84..4e73796e98ce 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -767,6 +767,9 @@ public void flush(ChannelHandlerContext ctx) throws Exception { sentFirstMessage = true; pendingUnencryptedWrites.writeAndRemoveAll(ctx); forceFlush(ctx); + // Explicit start handshake processing once we send the first message. This will also ensure + // we will schedule the timeout if needed. + startHandshakeProcessing(); return; } @@ -1661,14 +1664,16 @@ public void handlerAdded(final ChannelHandlerContext ctx) throws Exception { } private void startHandshakeProcessing() { - handshakeStarted = true; - if (engine.getUseClientMode()) { - // Begin the initial handshake. - // channelActive() event has been fired already, which means this.channelActive() will - // not be invoked. We have to initialize here instead. - handshake(null); - } else { - applyHandshakeTimeout(null); + if (!handshakeStarted) { + handshakeStarted = true; + if (engine.getUseClientMode()) { + // Begin the initial handshake. + // channelActive() event has been fired already, which means this.channelActive() will + // not be invoked. We have to initialize here instead. + handshake(null, true); + } else { + applyHandshakeTimeout(null); + } } } @@ -1702,13 +1707,13 @@ public Future renegotiate(final Promise promise) { executor.execute(new Runnable() { @Override public void run() { - handshake(promise); + handshake(promise, false); } }); return promise; } - handshake(promise); + handshake(promise, false); return promise; } @@ -1719,7 +1724,7 @@ public void run() { * assuming that the current negotiation has not been finished. * Currently, {@code null} is expected only for the initial handshake. */ - private void handshake(final Promise newHandshakePromise) { + private void handshake(final Promise newHandshakePromise, boolean initialHandshake) { final Promise p; if (newHandshakePromise != null) { final Promise oldHandshakePromise = handshakePromise; @@ -1741,6 +1746,11 @@ public void operationComplete(Future future) throws Exception { handshakePromise = p = newHandshakePromise; } else if (engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) { + if (initialHandshake) { + // This is the intial handshake either triggered by handlerAdded(...), channelActive(...) or + // flush(...) when starttls was used. In all the cases we need to ensure we schedule a timeout. + applyHandshakeTimeout(null); + } // Not all SSLEngine implementations support calling beginHandshake multiple times while a handshake // is in progress. See https://github.com/netty/netty/issues/4718. return; diff --git a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java index ab7a63c908cd..772a15f9ebcf 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java @@ -74,9 +74,7 @@ import javax.net.ssl.SSLProtocolException; import static io.netty.buffer.Unpooled.wrappedBuffer; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; @@ -754,4 +752,76 @@ public void operationComplete(ChannelFuture future) throws Exception { ReferenceCountUtil.release(sslClientCtx); } } + + @Test(timeout = 10000) + public void testHandshakeTimeoutFlushStartsHandshake() throws Exception { + testHandshakeTimeout0(false); + } + + @Test(timeout = 10000) + public void testHandshakeTimeoutStartTLS() throws Exception { + testHandshakeTimeout0(true); + } + + private static void testHandshakeTimeout0(final boolean startTls) throws Exception { + final SslContext sslClientCtx = SslContextBuilder.forClient() + .startTls(true) + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(SslProvider.JDK).build(); + + EventLoopGroup group = new NioEventLoopGroup(); + Channel sc = null; + Channel cc = null; + final SslHandler sslHandler = sslClientCtx.newHandler(UnpooledByteBufAllocator.DEFAULT); + sslHandler.setHandshakeTimeout(500, TimeUnit.MILLISECONDS); + + try { + sc = new ServerBootstrap() + .group(group) + .channel(NioServerSocketChannel.class) + .childHandler(new ChannelInboundHandlerAdapter()) + .bind(new InetSocketAddress(0)).syncUninterruptibly().channel(); + + ChannelFuture future = new Bootstrap() + .group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) throws Exception { + ch.pipeline().addLast(sslHandler); + if (startTls) { + ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ctx.writeAndFlush(wrappedBuffer(new byte[] { 1, 2, 3, 4 })); + } + }); + } + } + }).connect(sc.localAddress()); + if (!startTls) { + future.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + // Write something to trigger the handshake before fireChannelActive is called. + future.channel().writeAndFlush(wrappedBuffer(new byte [] { 1, 2, 3, 4 })); + } + }); + } + cc = future.syncUninterruptibly().channel(); + + Throwable cause = sslHandler.handshakeFuture().await().cause(); + assertThat(cause, CoreMatchers.instanceOf(SSLException.class)); + assertThat(cause.getMessage(), containsString("timed out")); + } finally { + if (cc != null) { + cc.close().syncUninterruptibly(); + } + if (sc != null) { + sc.close().syncUninterruptibly(); + } + group.shutdownGracefully(); + ReferenceCountUtil.release(sslClientCtx); + } + } } From 804e1fa9ccf426603e6d478f63c63bc00baae0fa Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Tue, 13 Nov 2018 11:56:09 -0800 Subject: [PATCH 262/417] Fix ref-counting when CompositeByteBuf is used with retainedSlice() (#8497) Motivation: ByteBuf.retainedSlice() and similar methods produce sliced buffers with an independent refcount to the buffer that they wrap. One of the optimizations in 10539f4dc738c48e8d63c46b72ca32906d7f40ec was to use the ref to the unwrapped buffer object for added slices, but this did not take into account the above special case when later releasing. Thanks to @rkapsi for discovering this via #8495. Modifications: Since a reference to the slice is still kept in the Component class, just changed Component.freeIfNecessary() to release the slice in preference to the unwrapped buf. Also added a unit test which reproduces the bug. Result: Fixes #8495 --- .../io/netty/buffer/CompositeByteBuf.java | 13 +++++-- .../buffer/AbstractCompositeByteBufTest.java | 34 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 77ba13fcaecf..c9212d5c6e7e 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -513,11 +513,11 @@ private void updateComponentOffsets(int cIndex) { public CompositeByteBuf removeComponent(int cIndex) { checkComponentIndex(cIndex); Component comp = components[cIndex]; - removeComp(cIndex); if (lastAccessed == comp) { lastAccessed = null; } comp.freeIfNecessary(); + removeComp(cIndex); if (comp.length() > 0) { // Only need to call updateComponentOffsets if the length was > 0 updateComponentOffsets(cIndex); @@ -1815,7 +1815,15 @@ ByteBuf duplicate() { } void freeIfNecessary() { - buf.release(); // We should not get a NPE here. If so, it must be a bug. + // Release the slice if present since it may have a different + // refcount to the unwrapped buf if it is a PooledSlicedByteBuf + ByteBuf buffer = slice; + if (buffer != null) { + buffer.release(); + } else { + buf.release(); + } + // null out in either case since it could be racy slice = null; } } @@ -2181,7 +2189,6 @@ private void removeCompRange(int from, int to) { } int newSize = size - to + from; for (int i = newSize; i < size; i++) { - components[i].slice = null; components[i] = null; } componentCount = newSize; diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java index 363b25d8c74c..8e0f148cdf0d 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java @@ -1144,6 +1144,40 @@ public void testReleasesItsComponents() { assertEquals(0, buffer.refCnt()); } + @Test + public void testReleasesItsComponents2() { + // It is important to use a pooled allocator here to ensure + // the slices returned by readRetainedSlice are of type + // PooledSlicedByteBuf, which maintains an independent refcount + // (so that we can be sure to cover this case) + ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer(); // 1 + + buffer.writeBytes(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); + + // use readRetainedSlice this time - produces different kind of slices + ByteBuf s1 = buffer.readRetainedSlice(2); // 2 + ByteBuf s2 = s1.readRetainedSlice(2); // 3 + ByteBuf s3 = s2.readRetainedSlice(2); // 4 + ByteBuf s4 = s3.readRetainedSlice(2); // 5 + + ByteBuf composite = Unpooled.compositeBuffer() + .addComponent(s1) + .addComponents(s2, s3, s4) + .order(ByteOrder.LITTLE_ENDIAN); + + assertEquals(1, composite.refCnt()); + assertEquals(2, buffer.refCnt()); + + // releasing composite should release the 4 components + composite.release(); + assertEquals(0, composite.refCnt()); + assertEquals(1, buffer.refCnt()); + + // last remaining ref to buffer + buffer.release(); + assertEquals(0, buffer.refCnt()); + } + @Test public void testReleasesOnShrink() { From 044515f3697c72b39aa881037cbb89ff8f5b57ca Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Wed, 14 Nov 2018 00:17:43 -0700 Subject: [PATCH 263/417] Defer HTTP/2 stream transition state on initial write until headers are written (#8471) Motivation: When the DefaultHttp2ConnectionEncoder writes the initial headers for a new locally created stream we create the stream in the half-closed state if the end-stream flag is set which signals to the life cycle manager that the headers have been sent. However, if we synchronously fail to write the headers the life cycle manager then sends a RST_STREAM on our behalf which is a connection level PROTOCOL_ERROR because the peer sees the stream in an IDLE state. Modification: Don't open the stream in the half-closed state if the end-stream flag is set and let the life cycle manager take care of it. Result: Cleaner state management in the DefaultHttp2ConnectionEncoder. Fixes #8434. --- .../http2/DefaultHttp2ConnectionEncoder.java | 7 ++++- .../DefaultHttp2ConnectionEncoderTest.java | 31 +++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java index 351f0e63840e..c21081094b33 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java @@ -163,7 +163,12 @@ public ChannelFuture writeHeaders(final ChannelHandlerContext ctx, final int str Http2Stream stream = connection.stream(streamId); if (stream == null) { try { - stream = connection.local().createStream(streamId, endOfStream); + // We don't create the stream in a `halfClosed` state because if this is an initial + // HEADERS frame we don't want the connection state to signify that the HEADERS have + // been sent until after they have been encoded and placed in the outbound buffer. + // Therefore, we let the `LifeCycleManager` will take care of transitioning the state + // as appropriate. + stream = connection.local().createStream(streamId, /*endOfStream*/ false); } catch (Http2Exception cause) { if (connection.remote().mayHaveCreatedStream(streamId)) { promise.tryFailure(new IllegalStateException("Stream no longer exists: " + streamId, cause)); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java index cf28944a77ed..6be47462c3c0 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java @@ -710,7 +710,7 @@ public void headersWriteShouldHalfClosePushStream() throws Exception { } @Test - public void headersWriteShouldHalfCloseAfterOnError() throws Exception { + public void headersWriteShouldHalfCloseAfterOnErrorForPreCreatedStream() throws Exception { final ChannelPromise promise = newPromise(); final Throwable ex = new RuntimeException(); // Fake an encoding error, like HPACK's HeaderListSizeException @@ -725,11 +725,38 @@ public ChannelFuture answer(InvocationOnMock invocation) { }); writeAllFlowControlledFrames(); - createStream(STREAM_ID, false); + Http2Stream stream = createStream(STREAM_ID, false); + encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise); + + assertTrue(promise.isDone()); + assertFalse(promise.isSuccess()); + assertFalse(stream.isHeadersSent()); + InOrder inOrder = inOrder(lifecycleManager); + inOrder.verify(lifecycleManager).onError(eq(ctx), eq(true), eq(ex)); + inOrder.verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(promise)); + } + + @Test + public void headersWriteShouldHalfCloseAfterOnErrorForImplicitlyCreatedStream() throws Exception { + final ChannelPromise promise = newPromise(); + final Throwable ex = new RuntimeException(); + // Fake an encoding error, like HPACK's HeaderListSizeException + when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), + eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true), eq(promise))) + .thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocation) { + promise.setFailure(ex); + return promise; + } + }); + + writeAllFlowControlledFrames(); encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise); assertTrue(promise.isDone()); assertFalse(promise.isSuccess()); + assertFalse(stream(STREAM_ID).isHeadersSent()); InOrder inOrder = inOrder(lifecycleManager); inOrder.verify(lifecycleManager).onError(eq(ctx), eq(true), eq(ex)); inOrder.verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(promise)); From 11ec7d892e8eca8dc6e05062d02a9f59b6883dce Mon Sep 17 00:00:00 2001 From: Tim Brooks Date: Wed, 14 Nov 2018 00:19:06 -0700 Subject: [PATCH 264/417] Cleanup SslHandler handshake/renegotiation (#8555) Motivation: The code for initiating a TLS handshake or renegotiation process is currently difficult to reason about. Modifications: This commit introduces to clear paths for starting a handshake. The first path is a normal handshake. The handshake is started and a timeout is scheduled. The second path is renegotiation. If the first handshake is incomplete, the renegotiation promise is added as a listener to the handshake promise. Otherwise, the renegotiation promise replaces the original promsie. At that point the handshake is started again and a timeout is scheduled. Result: Cleaner and easier to understand code. --- .../java/io/netty/handler/ssl/SslHandler.java | 74 +++++++------------ 1 file changed, 28 insertions(+), 46 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index 4e73796e98ce..f375bc2358c5 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -42,6 +42,7 @@ import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.ImmediateExecutor; import io.netty.util.concurrent.Promise; +import io.netty.util.concurrent.PromiseNotifier; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.ThrowableUtil; import io.netty.util.internal.UnstableApi; @@ -1670,10 +1671,9 @@ private void startHandshakeProcessing() { // Begin the initial handshake. // channelActive() event has been fired already, which means this.channelActive() will // not be invoked. We have to initialize here instead. - handshake(null, true); - } else { - applyHandshakeTimeout(null); + handshake(); } + applyHandshakeTimeout(); } } @@ -1707,50 +1707,34 @@ public Future renegotiate(final Promise promise) { executor.execute(new Runnable() { @Override public void run() { - handshake(promise, false); + renegotiateOnEventLoop(promise); } }); return promise; } - handshake(promise, false); + renegotiateOnEventLoop(promise); return promise; } + private void renegotiateOnEventLoop(final Promise newHandshakePromise) { + final Promise oldHandshakePromise = handshakePromise; + if (!oldHandshakePromise.isDone()) { + // There's no need to handshake because handshake is in progress already. + // Merge the new promise into the old one. + oldHandshakePromise.addListener(new PromiseNotifier>(newHandshakePromise)); + } else { + handshakePromise = newHandshakePromise; + handshake(); + applyHandshakeTimeout(); + } + } + /** * Performs TLS (re)negotiation. - * - * @param newHandshakePromise if {@code null}, use the existing {@link #handshakePromise}, - * assuming that the current negotiation has not been finished. - * Currently, {@code null} is expected only for the initial handshake. */ - private void handshake(final Promise newHandshakePromise, boolean initialHandshake) { - final Promise p; - if (newHandshakePromise != null) { - final Promise oldHandshakePromise = handshakePromise; - if (!oldHandshakePromise.isDone()) { - // There's no need to handshake because handshake is in progress already. - // Merge the new promise into the old one. - oldHandshakePromise.addListener(new FutureListener() { - @Override - public void operationComplete(Future future) throws Exception { - if (future.isSuccess()) { - newHandshakePromise.setSuccess(future.getNow()); - } else { - newHandshakePromise.setFailure(future.cause()); - } - } - }); - return; - } - - handshakePromise = p = newHandshakePromise; - } else if (engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) { - if (initialHandshake) { - // This is the intial handshake either triggered by handlerAdded(...), channelActive(...) or - // flush(...) when starttls was used. In all the cases we need to ensure we schedule a timeout. - applyHandshakeTimeout(null); - } + private void handshake() { + if (engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) { // Not all SSLEngine implementations support calling beginHandshake multiple times while a handshake // is in progress. See https://github.com/netty/netty/issues/4718. return; @@ -1763,8 +1747,6 @@ public void operationComplete(Future future) throws Exception { // call fireChannelActive() on the pipeline. return; } - // Forced to reuse the old handshake. - p = handshakePromise; } // Begin handshake. @@ -1775,27 +1757,27 @@ public void operationComplete(Future future) throws Exception { } catch (Throwable e) { setHandshakeFailure(ctx, e); } finally { - forceFlush(ctx); + forceFlush(ctx); } - applyHandshakeTimeout(p); } - private void applyHandshakeTimeout(Promise p) { - final Promise promise = p == null ? handshakePromise : p; + private void applyHandshakeTimeout() { + final Promise localHandshakePromise = this.handshakePromise; + // Set timeout if necessary. final long handshakeTimeoutMillis = this.handshakeTimeoutMillis; - if (handshakeTimeoutMillis <= 0 || promise.isDone()) { + if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) { return; } final ScheduledFuture timeoutFuture = ctx.executor().schedule(new Runnable() { @Override public void run() { - if (promise.isDone()) { + if (localHandshakePromise.isDone()) { return; } try { - if (handshakePromise.tryFailure(HANDSHAKE_TIMED_OUT)) { + if (localHandshakePromise.tryFailure(HANDSHAKE_TIMED_OUT)) { SslUtils.handleHandshakeFailure(ctx, HANDSHAKE_TIMED_OUT, true); } } finally { @@ -1805,7 +1787,7 @@ public void run() { }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS); // Cancel the handshake timeout when handshake is finished. - promise.addListener(new FutureListener() { + localHandshakePromise.addListener(new FutureListener() { @Override public void operationComplete(Future f) throws Exception { timeoutFuture.cancel(false); From d1654484c16dfb650ec40c258b6edcfbe94dea80 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 14 Nov 2018 08:49:13 +0100 Subject: [PATCH 265/417] Correctly convert between openssl / boringssl and java cipher names when using TLSv1.3 (#8485) Motivation: We did not correctly convert between openssl / boringssl and java ciphers when using TLV1.3 which had different effects when either using openssl or boringssl. - When using openssl and TLSv1.3 we always returned SSL_NULL_WITH_NULL_NULL as cipher name - When using boringssl with TLSv1.3 we always returned an incorrect constructed cipher name which does not match what is defined by Java. Modifications: - Add correct mappings in CipherSuiteConverter for both openssl and boringssl - Add unit tests for CipherSuiteConvert - Add unit in SSLEngine which checks that we do not return SSL_NULL_WITH_NULL_NULL ever and that server and client returns the same cipher name. Result: Fixes https://github.com/netty/netty/issues/8477. --- .../handler/ssl/CipherSuiteConverter.java | 76 ++++++++++-- .../java/io/netty/handler/ssl/OpenSsl.java | 30 +++-- .../ssl/ReferenceCountedOpenSslContext.java | 2 +- .../ssl/ReferenceCountedOpenSslEngine.java | 8 +- .../java/io/netty/handler/ssl/SslUtils.java | 6 +- .../handler/ssl/CipherSuiteConverterTest.java | 51 ++++++-- .../io/netty/handler/ssl/SSLEngineTest.java | 117 ++++++++++++++---- 7 files changed, 229 insertions(+), 61 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java b/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java index deeeb0cfeb38..94e951f7f551 100644 --- a/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java +++ b/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java @@ -20,12 +20,15 @@ import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static java.util.Collections.singletonMap; + /** * Converts a Java cipher suite string to an OpenSSL cipher suite string and vice versa. * @@ -95,6 +98,26 @@ final class CipherSuiteConverter { */ private static final ConcurrentMap> o2j = PlatformDependent.newConcurrentHashMap(); + private static final Map j2oTls13; + private static final Map> o2jTls13; + + static { + Map j2oTls13Map = new HashMap(); + j2oTls13Map.put("TLS_AES_128_GCM_SHA256", "AEAD-AES128-GCM-SHA256"); + j2oTls13Map.put("TLS_AES_256_GCM_SHA384", "AEAD-AES256-GCM-SHA384"); + j2oTls13Map.put("TLS_CHACHA20_POLY1305_SHA256", "AEAD-CHACHA20-POLY1305-SHA256"); + j2oTls13 = Collections.unmodifiableMap(j2oTls13Map); + + Map> o2jTls13Map = new HashMap>(); + o2jTls13Map.put("TLS_AES_128_GCM_SHA256", singletonMap("TLS", "TLS_AES_128_GCM_SHA256")); + o2jTls13Map.put("TLS_AES_256_GCM_SHA384", singletonMap("TLS", "TLS_AES_256_GCM_SHA384")); + o2jTls13Map.put("TLS_CHACHA20_POLY1305_SHA256", singletonMap("TLS", "TLS_CHACHA20_POLY1305_SHA256")); + o2jTls13Map.put("AEAD-AES128-GCM-SHA256", singletonMap("TLS", "TLS_AES_128_GCM_SHA256")); + o2jTls13Map.put("AEAD-AES256-GCM-SHA384", singletonMap("TLS", "TLS_AES_256_GCM_SHA384")); + o2jTls13Map.put("AEAD-CHACHA20-POLY1305-SHA256", singletonMap("TLS", "TLS_CHACHA20_POLY1305_SHA256")); + o2jTls13 = Collections.unmodifiableMap(o2jTls13Map); + } + /** * Clears the cache for testing purpose. */ @@ -127,17 +150,21 @@ static boolean isO2JCached(String key, String protocol, String value) { * * @return {@code null} if the conversion has failed */ - static String toOpenSsl(String javaCipherSuite) { + static String toOpenSsl(String javaCipherSuite, boolean boringSSL) { String converted = j2o.get(javaCipherSuite); if (converted != null) { return converted; - } else { - return cacheFromJava(javaCipherSuite); } + return cacheFromJava(javaCipherSuite, boringSSL); } - private static String cacheFromJava(String javaCipherSuite) { - String openSslCipherSuite = toOpenSslUncached(javaCipherSuite); + private static String cacheFromJava(String javaCipherSuite, boolean boringSSL) { + String converted = j2oTls13.get(javaCipherSuite); + if (converted != null) { + return boringSSL ? converted : javaCipherSuite; + } + + String openSslCipherSuite = toOpenSslUncached(javaCipherSuite, boringSSL); if (openSslCipherSuite == null) { return null; } @@ -158,7 +185,12 @@ private static String cacheFromJava(String javaCipherSuite) { return openSslCipherSuite; } - static String toOpenSslUncached(String javaCipherSuite) { + static String toOpenSslUncached(String javaCipherSuite, boolean boringSSL) { + String converted = j2oTls13.get(javaCipherSuite); + if (converted != null) { + return boringSSL ? converted : javaCipherSuite; + } + Matcher m = JAVA_CIPHERSUITE_PATTERN.matcher(javaCipherSuite); if (!m.matches()) { return null; @@ -260,14 +292,23 @@ static String toJava(String openSslCipherSuite, String protocol) { String javaCipherSuite = p2j.get(protocol); if (javaCipherSuite == null) { - javaCipherSuite = protocol + '_' + p2j.get(""); + String cipher = p2j.get(""); + if (cipher == null) { + return null; + } + javaCipherSuite = protocol + '_' + cipher; } return javaCipherSuite; } private static Map cacheFromOpenSsl(String openSslCipherSuite) { - String javaCipherSuiteSuffix = toJavaUncached(openSslCipherSuite); + Map converted = o2jTls13.get(openSslCipherSuite); + if (converted != null) { + return converted; + } + + String javaCipherSuiteSuffix = toJavaUncached0(openSslCipherSuite, false); if (javaCipherSuiteSuffix == null) { return null; } @@ -293,6 +334,17 @@ private static Map cacheFromOpenSsl(String openSslCipherSuite) { } static String toJavaUncached(String openSslCipherSuite) { + return toJavaUncached0(openSslCipherSuite, true); + } + + private static String toJavaUncached0(String openSslCipherSuite, boolean checkTls13) { + if (checkTls13) { + Map converted = o2jTls13.get(openSslCipherSuite); + if (converted != null) { + return converted.get("TLS"); + } + } + Matcher m = OPENSSL_CIPHERSUITE_PATTERN.matcher(openSslCipherSuite); if (!m.matches()) { return null; @@ -402,14 +454,14 @@ private static String toJavaHmacAlgo(String hmacAlgo) { * guaranteed that at least one of the {@link StringBuilder}s contain some ciphers that can be used to configure * OpenSSL. */ - static void convertToCipherStrings( - Iterable cipherSuites, StringBuilder cipherBuilder, StringBuilder cipherTLSv13Builder) { + static void convertToCipherStrings(Iterable cipherSuites, StringBuilder cipherBuilder, + StringBuilder cipherTLSv13Builder, boolean boringSSL) { for (String c: cipherSuites) { if (c == null) { break; } - String converted = toOpenSsl(c); + String converted = toOpenSsl(c, boringSSL); if (converted == null) { converted = c; } @@ -418,7 +470,7 @@ static void convertToCipherStrings( throw new IllegalArgumentException("unsupported cipher suite: " + c + '(' + converted + ')'); } - if (SslUtils.isTLSv13Cipher(converted)) { + if (SslUtils.isTLSv13Cipher(converted) || SslUtils.isTLSv13Cipher(c)) { cipherTLSv13Builder.append(converted); cipherTLSv13Builder.append(':'); } else { diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java index 67ca5ca91fe9..78a528ff985d 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java @@ -61,11 +61,6 @@ public final class OpenSsl { private static final boolean SUPPORTS_HOSTNAME_VALIDATION; private static final boolean USE_KEYMANAGER_FACTORY; private static final boolean SUPPORTS_OCSP; - private static final String TLSV13_CIPHERS = "TLS_AES_256_GCM_SHA384" + ':' + - "TLS_CHACHA20_POLY1305_SHA256" + ':' + - "TLS_AES_128_GCM_SHA256" + ':' + - "TLS_AES_128_CCM_8_SHA256" + ':' + - "TLS_AES_128_CCM_SHA256"; private static final boolean TLSV13_SUPPORTED; private static final boolean IS_BORINGSSL; static final Set SUPPORTED_PROTOCOLS_SET; @@ -149,8 +144,22 @@ public final class OpenSsl { long certBio = 0; try { try { - SSLContext.setCipherSuite(sslCtx, TLSV13_CIPHERS, true); - tlsv13Supported = true; + StringBuilder tlsv13Ciphers = new StringBuilder(); + + for (String cipher: TLSV13_CIPHERS) { + String converted = CipherSuiteConverter.toOpenSsl(cipher, IS_BORINGSSL); + if (converted != null) { + tlsv13Ciphers.append(converted).append(':'); + } + } + if (tlsv13Ciphers.length() == 0) { + tlsv13Supported = false; + } else { + tlsv13Ciphers.setLength(tlsv13Ciphers.length() - 1); + SSLContext.setCipherSuite(sslCtx, tlsv13Ciphers.toString() , true); + tlsv13Supported = true; + } + } catch (Exception ignore) { tlsv13Supported = false; } @@ -174,7 +183,10 @@ public final class OpenSsl { Collections.addAll(availableOpenSslCipherSuites, "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" , - "TLS_CHACHA20_POLY1305_SHA256"); + "TLS_CHACHA20_POLY1305_SHA256", + "AEAD-AES128-GCM-SHA256", + "AEAD-AES256-GCM-SHA384", + "AEAD-CHACHA20-POLY1305-SHA256"); } try { SSL.setHostNameValidation(ssl, 0, "netty.io"); @@ -461,7 +473,7 @@ public static Set availableJavaCipherSuites() { * Both Java-style cipher suite and OpenSSL-style cipher suite are accepted. */ public static boolean isCipherSuiteAvailable(String cipherSuite) { - String converted = CipherSuiteConverter.toOpenSsl(cipherSuite); + String converted = CipherSuiteConverter.toOpenSsl(cipherSuite, IS_BORINGSSL); if (converted != null) { cipherSuite = converted; } diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index 6f471b361a4b..59c166c842bb 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -250,7 +250,7 @@ public String run() { } } else { CipherSuiteConverter.convertToCipherStrings( - unmodifiableCiphers, cipherBuilder, cipherTLSv13Builder); + unmodifiableCiphers, cipherBuilder, cipherTLSv13Builder, OpenSsl.isBoringSSL()); // Set non TLSv1.3 ciphers. SSLContext.setCipherSuite(ctx, cipherBuilder.toString(), false); diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index d70c49ab90ed..c17fdfd9f03a 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -134,7 +134,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc private static final AtomicIntegerFieldUpdater DESTROYED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(ReferenceCountedOpenSslEngine.class, "destroyed"); - private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL"; private static final SSLEngineResult NEED_UNWRAP_OK = new SSLEngineResult(OK, NEED_UNWRAP, 0, 0); private static final SSLEngineResult NEED_UNWRAP_CLOSED = new SSLEngineResult(CLOSED, NEED_UNWRAP, 0, 0); private static final SSLEngineResult NEED_WRAP_OK = new SSLEngineResult(OK, NEED_WRAP, 0, 0); @@ -1409,7 +1408,7 @@ public final void setEnabledCipherSuites(String[] cipherSuites) { final StringBuilder buf = new StringBuilder(); final StringBuilder bufTLSv13 = new StringBuilder(); - CipherSuiteConverter.convertToCipherStrings(Arrays.asList(cipherSuites), buf, bufTLSv13); + CipherSuiteConverter.convertToCipherStrings(Arrays.asList(cipherSuites), buf, bufTLSv13, OpenSsl.isBoringSSL()); final String cipherSuiteSpec = buf.toString(); final String cipherSuiteSpecTLSv13 = bufTLSv13.toString(); @@ -1715,7 +1714,8 @@ private String toJavaCipherSuite(String openSslCipherSuite) { return null; } - String prefix = toJavaCipherSuitePrefix(SSL.getVersion(ssl)); + String version = SSL.getVersion(ssl); + String prefix = toJavaCipherSuitePrefix(version); return CipherSuiteConverter.toJava(openSslCipherSuite, prefix); } @@ -2238,7 +2238,7 @@ public Principal getLocalPrincipal() { public String getCipherSuite() { synchronized (ReferenceCountedOpenSslEngine.this) { if (cipher == null) { - return INVALID_CIPHER; + return SslUtils.INVALID_CIPHER; } return cipher; } diff --git a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java b/handler/src/main/java/io/netty/handler/ssl/SslUtils.java index 492c4f2b7d95..e7640365adc9 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslUtils.java @@ -29,7 +29,7 @@ import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -42,7 +42,7 @@ */ final class SslUtils { // See https://tools.ietf.org/html/rfc8446#appendix-B.4 - private static final Set TLSV13_CIPHERS = Collections.unmodifiableSet(new HashSet( + static final Set TLSV13_CIPHERS = Collections.unmodifiableSet(new LinkedHashSet( asList("TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256", "TLS_AES_128_GCM_SHA256", "TLS_AES_128_CCM_8_SHA256", "TLS_AES_128_CCM_SHA256"))); @@ -55,6 +55,8 @@ final class SslUtils { static final String PROTOCOL_TLS_V1_2 = "TLSv1.2"; static final String PROTOCOL_TLS_V1_3 = "TLSv1.3"; + static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL"; + /** * change cipher spec */ diff --git a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteConverterTest.java b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteConverterTest.java index f70da234c707..a0d425d0a7ac 100644 --- a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteConverterTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteConverterTest.java @@ -122,10 +122,14 @@ public void testJ2OMappings() throws Exception { testJ2OMapping("TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "ECDHE-PSK-CHACHA20-POLY1305"); testJ2OMapping("TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "DHE-PSK-CHACHA20-POLY1305"); testJ2OMapping("TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256", "RSA-PSK-CHACHA20-POLY1305"); + + testJ2OMapping("TLS_AES_128_GCM_SHA256", "TLS_AES_128_GCM_SHA256"); + testJ2OMapping("TLS_AES_256_GCM_SHA384", "TLS_AES_256_GCM_SHA384"); + testJ2OMapping("TLS_CHACHA20_POLY1305_SHA256", "TLS_CHACHA20_POLY1305_SHA256"); } private static void testJ2OMapping(String javaCipherSuite, String openSslCipherSuite) { - final String actual = CipherSuiteConverter.toOpenSslUncached(javaCipherSuite); + final String actual = CipherSuiteConverter.toOpenSslUncached(javaCipherSuite, false); logger.info("{} => {}", javaCipherSuite, actual); assertThat(actual, is(openSslCipherSuite)); } @@ -316,15 +320,18 @@ private static void testUnknownOpenSSLCiphersToJava(String openSslCipherSuite) { private static void testUnknownJavaCiphersToOpenSSL(String javaCipherSuite) { CipherSuiteConverter.clearCache(); - assertNull(CipherSuiteConverter.toOpenSsl(javaCipherSuite)); - assertNull(CipherSuiteConverter.toOpenSsl(javaCipherSuite)); + assertNull(CipherSuiteConverter.toOpenSsl(javaCipherSuite, false)); + assertNull(CipherSuiteConverter.toOpenSsl(javaCipherSuite, true)); } private static void testCachedJ2OMapping(String javaCipherSuite, String openSslCipherSuite) { CipherSuiteConverter.clearCache(); - final String actual1 = CipherSuiteConverter.toOpenSsl(javaCipherSuite); + // For TLSv1.3 this should make no diffierence if boringSSL is true or false + final String actual1 = CipherSuiteConverter.toOpenSsl(javaCipherSuite, false); assertThat(actual1, is(openSslCipherSuite)); + final String actual2 = CipherSuiteConverter.toOpenSsl(javaCipherSuite, true); + assertEquals(actual1, actual2); // Ensure that the cache entries have been created. assertThat(CipherSuiteConverter.isJ2OCached(javaCipherSuite, actual1), is(true)); @@ -332,12 +339,12 @@ private static void testCachedJ2OMapping(String javaCipherSuite, String openSslC assertThat(CipherSuiteConverter.isO2JCached(actual1, "SSL", "SSL_" + javaCipherSuite.substring(4)), is(true)); assertThat(CipherSuiteConverter.isO2JCached(actual1, "TLS", "TLS_" + javaCipherSuite.substring(4)), is(true)); - final String actual2 = CipherSuiteConverter.toOpenSsl(javaCipherSuite); - assertThat(actual2, is(openSslCipherSuite)); + final String actual3 = CipherSuiteConverter.toOpenSsl(javaCipherSuite, false); + assertThat(actual3, is(openSslCipherSuite)); // Test if the returned cipher strings are identical, // so that the TLS sessions with the same cipher suite do not create many strings. - assertThat(actual1, is(sameInstance(actual2))); + assertThat(actual1, is(sameInstance(actual3))); } @Test @@ -373,4 +380,34 @@ private static void testCachedO2JMapping(String javaCipherSuite, String openSslC assertThat(tlsActual1, is(sameInstance(tlsActual2))); assertThat(sslActual1, is(sameInstance(sslActual2))); } + + @Test + public void testTlsv13Mappings() { + CipherSuiteConverter.clearCache(); + + assertEquals("TLS_AES_128_GCM_SHA256", + CipherSuiteConverter.toJava("TLS_AES_128_GCM_SHA256", "TLS")); + assertNull(CipherSuiteConverter.toJava("TLS_AES_128_GCM_SHA256", "SSL")); + assertEquals("TLS_AES_256_GCM_SHA384", + CipherSuiteConverter.toJava("TLS_AES_256_GCM_SHA384", "TLS")); + assertNull(CipherSuiteConverter.toJava("TLS_AES_256_GCM_SHA384", "SSL")); + assertEquals("TLS_CHACHA20_POLY1305_SHA256", + CipherSuiteConverter.toJava("TLS_CHACHA20_POLY1305_SHA256", "TLS")); + assertNull(CipherSuiteConverter.toJava("TLS_CHACHA20_POLY1305_SHA256", "SSL")); + + // BoringSSL use different cipher naming then OpenSSL so we need to test for both + assertEquals("TLS_AES_128_GCM_SHA256", + CipherSuiteConverter.toOpenSsl("TLS_AES_128_GCM_SHA256", false)); + assertEquals("TLS_AES_256_GCM_SHA384", + CipherSuiteConverter.toOpenSsl("TLS_AES_256_GCM_SHA384", false)); + assertEquals("TLS_CHACHA20_POLY1305_SHA256", + CipherSuiteConverter.toOpenSsl("TLS_CHACHA20_POLY1305_SHA256", false)); + + assertEquals("AEAD-AES128-GCM-SHA256", + CipherSuiteConverter.toOpenSsl("TLS_AES_128_GCM_SHA256", true)); + assertEquals("AEAD-AES256-GCM-SHA384", + CipherSuiteConverter.toOpenSsl("TLS_AES_256_GCM_SHA384", true)); + assertEquals("AEAD-CHACHA20-POLY1305-SHA256", + CipherSuiteConverter.toOpenSsl("TLS_CHACHA20_POLY1305_SHA256", true)); + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 4de0bd8d0fb8..6e8a231c82e2 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -497,13 +497,16 @@ public void tearDown() throws InterruptedException { } @Test - public void testMutualAuthSameCerts() throws Exception { + public void testMutualAuthSameCerts() throws Throwable { mySetupMutualAuth(new File(getClass().getResource("test_unencrypted.pem").getFile()), new File(getClass().getResource("test.crt").getFile()), null); runTest(null); assertTrue(serverLatch.await(2, TimeUnit.SECONDS)); - assertNull(serverException); + Throwable cause = serverException; + if (cause != null) { + throw cause; + } } @Test @@ -931,9 +934,43 @@ private void mySetupMutualAuth(File keyFile, File crtFile, String keyPassword) mySetupMutualAuth(crtFile, keyFile, crtFile, keyPassword, crtFile, keyFile, crtFile, keyPassword); } + private void verifySSLSessionForMutualAuth(SSLSession session, File certFile, String principalName) + throws Exception { + InputStream in = null; + try { + assertEquals(principalName, session.getLocalPrincipal().getName()); + assertEquals(principalName, session.getPeerPrincipal().getName()); + assertNotNull(session.getId()); + assertEquals(protocolCipherCombo.cipher, session.getCipherSuite()); + assertEquals(protocolCipherCombo.protocol, session.getProtocol()); + assertTrue(session.getApplicationBufferSize() > 0); + assertTrue(session.getCreationTime() > 0); + assertTrue(session.isValid()); + assertTrue(session.getLastAccessedTime() > 0); + + in = new FileInputStream(certFile); + final byte[] certBytes = SslContext.X509_CERT_FACTORY + .generateCertificate(in).getEncoded(); + + // Verify session + assertEquals(1, session.getPeerCertificates().length); + assertArrayEquals(certBytes, session.getPeerCertificates()[0].getEncoded()); + + assertEquals(1, session.getPeerCertificateChain().length); + assertArrayEquals(certBytes, session.getPeerCertificateChain()[0].getEncoded()); + + assertEquals(1, session.getLocalCertificates().length); + assertArrayEquals(certBytes, session.getLocalCertificates()[0].getEncoded()); + } finally { + if (in != null) { + in.close(); + } + } + } + private void mySetupMutualAuth( File servertTrustCrtFile, File serverKeyFile, final File serverCrtFile, String serverKeyPassword, - File clientTrustCrtFile, File clientKeyFile, File clientCrtFile, String clientKeyPassword) + File clientTrustCrtFile, File clientKeyFile, final File clientCrtFile, String clientKeyPassword) throws InterruptedException, SSLException { serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile, serverKeyPassword) @@ -969,7 +1006,7 @@ protected void initChannel(Channel ch) { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ChannelPipeline p = ch.pipeline(); - SSLEngine engine = wrapEngine(serverSslCtx.newEngine(ch.alloc())); + final SSLEngine engine = wrapEngine(serverSslCtx.newEngine(ch.alloc())); engine.setUseClientMode(false); engine.setNeedClientAuth(true); @@ -991,27 +1028,8 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt == SslHandshakeCompletionEvent.SUCCESS) { try { - InputStream in = new FileInputStream(serverCrtFile); - try { - final byte[] cert = SslContext.X509_CERT_FACTORY - .generateCertificate(in).getEncoded(); - - // Verify session - SSLSession session = ctx.pipeline().get(SslHandler.class).engine().getSession(); - assertEquals(1, session.getPeerCertificates().length); - assertArrayEquals(cert, session.getPeerCertificates()[0].getEncoded()); - - assertEquals(1, session.getPeerCertificateChain().length); - assertArrayEquals(cert, session.getPeerCertificateChain()[0].getEncoded()); - - assertEquals(1, session.getLocalCertificates().length); - assertArrayEquals(cert, session.getLocalCertificates()[0].getEncoded()); - - assertEquals(PRINCIPAL_NAME, session.getLocalPrincipal().getName()); - assertEquals(PRINCIPAL_NAME, session.getPeerPrincipal().getName()); - } finally { - in.close(); - } + verifySSLSessionForMutualAuth( + engine.getSession(), serverCrtFile, PRINCIPAL_NAME); } catch (Throwable cause) { serverException = cause; } @@ -1029,12 +1047,24 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc protected void initChannel(Channel ch) throws Exception { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); - SslHandler handler = clientSslCtx.newHandler(ch.alloc()); + final SslHandler handler = clientSslCtx.newHandler(ch.alloc()); handler.engine().setNeedClientAuth(true); ChannelPipeline p = ch.pipeline(); p.addLast(handler); p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch)); p.addLast(new ChannelInboundHandlerAdapter() { + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt == SslHandshakeCompletionEvent.SUCCESS) { + try { + verifySSLSessionForMutualAuth( + handler.engine().getSession(), clientCrtFile, PRINCIPAL_NAME); + } catch (Throwable cause) { + clientException = cause; + } + } + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (cause.getCause() instanceof SSLHandshakeException) { @@ -2661,6 +2691,41 @@ public void testInvalidCipher() throws Exception { } } + @Test + public void testGetCiphersuite() throws Exception { + clientSslCtx = SslContextBuilder.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .sslContextProvider(clientSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); + SelfSignedCertificate ssc = new SelfSignedCertificate(); + serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) + .sslProvider(sslServerProvider()) + .sslContextProvider(serverSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); + SSLEngine clientEngine = null; + SSLEngine serverEngine = null; + try { + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + handshake(clientEngine, serverEngine); + + String clientCipher = clientEngine.getSession().getCipherSuite(); + String serverCipher = serverEngine.getSession().getCipherSuite(); + assertEquals(clientCipher, serverCipher); + + assertEquals(protocolCipherCombo.cipher, clientCipher); + } finally { + cleanupClientSslEngine(clientEngine); + cleanupServerSslEngine(serverEngine); + ssc.delete(); + } + } + protected SSLEngine wrapEngine(SSLEngine engine) { return engine; } From 0d2e38d5d6db6ed1abdf901230fa524c0f8db14c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 14 Nov 2018 19:23:11 +0100 Subject: [PATCH 266/417] Correctly convert supported signature algorithms when using BoringSSL (#8481) * Correctly convert supported signature algorithms when using BoringSSL Motivation: BoringSSL uses different naming schemes for the signature algorithms so we need to adjust the regex to also handle these. Modifications: - Adjust SignatureAlgorithmConverter to handle BoringSSL naming scheme - Ensure we do not include duplicates - Add unit tests. Result: Correctly convert boringssl signature algorithm names. --- .../ssl/ReferenceCountedOpenSslEngine.java | 5 +++- .../ssl/SignatureAlgorithmConverter.java | 25 ++++++++++++++----- .../ssl/SignatureAlgorithmConverterTest.java | 15 +++++++++++ 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index c17fdfd9f03a..cf98743379ba 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -42,8 +42,10 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.locks.Lock; @@ -287,9 +289,10 @@ public String[] getPeerSupportedSignatureAlgorithms() { if (algs == null) { peerSupportedSignatureAlgorithms = EmptyArrays.EMPTY_STRINGS; } else { - List algorithmList = new ArrayList(algs.length); + Set algorithmList = new LinkedHashSet(algs.length); for (String alg: algs) { String converted = SignatureAlgorithmConverter.toJavaName(alg); + if (converted != null) { algorithmList.add(converted); } diff --git a/handler/src/main/java/io/netty/handler/ssl/SignatureAlgorithmConverter.java b/handler/src/main/java/io/netty/handler/ssl/SignatureAlgorithmConverter.java index 70a13516d44e..c68a5f9a51c6 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SignatureAlgorithmConverter.java +++ b/handler/src/main/java/io/netty/handler/ssl/SignatureAlgorithmConverter.java @@ -35,8 +35,17 @@ private SignatureAlgorithmConverter() { } // dsa_with_SHA224 // // For more details see https://github.com/openssl/openssl/blob/OpenSSL_1_0_2p/crypto/objects/obj_dat.h + // + // BoringSSL uses a different format: + // https://github.com/google/boringssl/blob/8525ff3/ssl/ssl_privkey.cc#L436 + // private static final Pattern PATTERN = Pattern.compile( - "((^[a-zA-Z].+)With(.+)Encryption$)|((^[a-zA-Z].+)(_with_|-with-)(.+$))"); + // group 1 - 2 + "(?:(^[a-zA-Z].+)With(.+)Encryption$)|" + + // group 3 - 4 + "(?:(^[a-zA-Z].+)(?:_with_|-with-|_pkcs1_|_pss_rsae_)(.+$))|" + + // group 5 - 6 + "(?:(^[a-zA-Z].+)_(.+$))"); /** * Converts an OpenSSL algorithm name to a Java algorithm name and return it, @@ -48,12 +57,16 @@ static String toJavaName(String opensslName) { } Matcher matcher = PATTERN.matcher(opensslName); if (matcher.matches()) { - String group2 = matcher.group(2); - if (group2 != null) { - return group2.toUpperCase(Locale.ROOT) + "with" + matcher.group(3).toUpperCase(Locale.ROOT); + String group1 = matcher.group(1); + if (group1 != null) { + return group1.toUpperCase(Locale.ROOT) + "with" + matcher.group(2).toUpperCase(Locale.ROOT); } - if (matcher.group(4) != null) { - return matcher.group(7).toUpperCase(Locale.ROOT) + "with" + matcher.group(5).toUpperCase(Locale.ROOT); + if (matcher.group(3) != null) { + return matcher.group(4).toUpperCase(Locale.ROOT) + "with" + matcher.group(3).toUpperCase(Locale.ROOT); + } + + if (matcher.group(5) != null) { + return matcher.group(6).toUpperCase(Locale.ROOT) + "with" + matcher.group(5).toUpperCase(Locale.ROOT); } } return null; diff --git a/handler/src/test/java/io/netty/handler/ssl/SignatureAlgorithmConverterTest.java b/handler/src/test/java/io/netty/handler/ssl/SignatureAlgorithmConverterTest.java index 199b50b395f9..0b8ce3a03e6b 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SignatureAlgorithmConverterTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SignatureAlgorithmConverterTest.java @@ -37,6 +37,21 @@ public void testWithUnderscore() { assertEquals("SHA256withDSA", SignatureAlgorithmConverter.toJavaName("dsa_with_SHA256")); } + @Test + public void testBoringSSLOneUnderscore() { + assertEquals("SHA256withECDSA", SignatureAlgorithmConverter.toJavaName("ecdsa_sha256")); + } + + @Test + public void testBoringSSLPkcs1() { + assertEquals("SHA256withRSA", SignatureAlgorithmConverter.toJavaName("rsa_pkcs1_sha256")); + } + + @Test + public void testBoringSSLPSS() { + assertEquals("SHA256withRSA", SignatureAlgorithmConverter.toJavaName("rsa_pss_rsae_sha256")); + } + @Test public void testInvalid() { assertNull(SignatureAlgorithmConverter.toJavaName("ThisIsSomethingInvalid")); From 845a65b31c93eadd86414e6d0a753cd7a93b04c1 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 15 Nov 2018 07:19:28 +0100 Subject: [PATCH 267/417] Nio|Epoll|KqueueEventLoop task execution might throw UnsupportedOperationException on shutdown. (#8476) Motivation: There is a racy UnsupportedOperationException instead because the task removal is delegated to MpscChunkedArrayQueue that does not support removal. This happens with SingleThreadEventExecutor that overrides the newTaskQueue to return an MPSC queue instead of the LinkedBlockingQueue returned by the base class such as NioEventLoop, EpollEventLoop and KQueueEventLoop. Modifications: - Catch the UnsupportedOperationException - Add unit test. Result: Fix #8475 --- .../concurrent/SingleThreadEventExecutor.java | 16 +++++++- .../netty/channel/nio/NioEventLoopTest.java | 40 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java index 67959016152d..dab7e9500d01 100644 --- a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java +++ b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java @@ -778,8 +778,20 @@ public void execute(Runnable task) { addTask(task); if (!inEventLoop) { startThread(); - if (isShutdown() && removeTask(task)) { - reject(); + if (isShutdown()) { + boolean reject = false; + try { + if (removeTask(task)) { + reject = true; + } + } catch (UnsupportedOperationException e) { + // The task queue does not support removal so the best thing we can do is to just move on and + // hope we will be able to pick-up the task before its completely terminated. + // In worst case we will log on termination. + } + if (reject) { + reject(); + } } } diff --git a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java index d3412c2d9963..15fcb2124426 100644 --- a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java +++ b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java @@ -22,6 +22,7 @@ import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.util.concurrent.Future; +import org.hamcrest.core.IsInstanceOf; import org.junit.Test; import java.net.InetSocketAddress; @@ -29,7 +30,9 @@ import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.*; @@ -171,4 +174,41 @@ public void channelUnregistered(SocketChannel ch, Throwable cause) { group.shutdownGracefully(); } } + + @SuppressWarnings("deprecation") + @Test + public void testTaskRemovalOnShutdownThrowsNoUnsupportedOperationException() throws Exception { + final AtomicReference error = new AtomicReference(); + final Runnable task = new Runnable() { + @Override + public void run() { + // NOOP + } + }; + // Just run often enough to trigger it normally. + for (int i = 0; i < 1000; i++) { + NioEventLoopGroup group = new NioEventLoopGroup(1); + final NioEventLoop loop = (NioEventLoop) group.next(); + + Thread t = new Thread(new Runnable() { + @Override + public void run() { + try { + for (;;) { + loop.execute(task); + } + } catch (Throwable cause) { + error.set(cause); + } + } + }); + t.start(); + group.shutdownNow(); + t.join(); + group.terminationFuture().syncUninterruptibly(); + assertThat(error.get(), IsInstanceOf.instanceOf(RejectedExecutionException.class)); + error.set(null); + } + } + } From 76673619246dcd0ca274bb2a9736bc1eccd4519f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 15 Nov 2018 18:05:15 +0100 Subject: [PATCH 268/417] Update to netty-tcnative 2.0.20.Final (#8561) Motivation: Update to netty-tcnative 2.0.20.Final which fixed a bug related to retrieving the remote signature algorithms when using BoringSSL. Modifications: Update netty-tcnative Result: Be able to correctly detect the remote signature algorithms when using BoringSSL. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 59e442502c37..6dfb062c474b 100644 --- a/pom.xml +++ b/pom.xml @@ -248,7 +248,7 @@ fedora netty-tcnative - 2.0.19.Final + 2.0.20.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber From 20d4fda55e2b5f05b256af6844b5cc6b8c62b62f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 16 Nov 2018 07:37:57 +0100 Subject: [PATCH 269/417] Return the correct pointer from ReferenceCountedOpenSslContext.context() and sslCtxPointer() (#8562) Motivation: We did not return the pointer to SSL_CTX put to the internal datastructure of tcnative. Modifications: Return the correct pointer. Result: Methods work as documented in the javadocs. --- .../handler/ssl/ReferenceCountedOpenSslContext.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index 59c166c842bb..da1fdb139ba2 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -421,13 +421,7 @@ public final SSLEngine newEngine(ByteBufAllocator alloc) { */ @Deprecated public final long context() { - Lock readerLock = ctxLock.readLock(); - readerLock.lock(); - try { - return ctx; - } finally { - readerLock.unlock(); - } + return sslCtxPointer(); } /** @@ -502,7 +496,7 @@ public final long sslCtxPointer() { Lock readerLock = ctxLock.readLock(); readerLock.lock(); try { - return ctx; + return SSLContext.getSslCtx(ctx); } finally { readerLock.unlock(); } From 8d4d76d2163fe65ab35a5c206ed4c1348e86d671 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 16 Nov 2018 07:38:32 +0100 Subject: [PATCH 270/417] ReferenceCountedOpenSslEngine SSLSession.getLocalCertificates() / getLocalPrincipial() did not work when KeyManagerFactory was used. (#8560) Motivation: The SSLSession.getLocalCertificates() / getLocalPrincipial() methods did not correctly return the local configured certificate / principal if a KeyManagerFactory was used when configure the SslContext. Modifications: - Correctly update the local certificates / principial when the key material is selected. - Add test case that verifies the SSLSession after the handshake to ensure we correctly return all values. Result: SSLSession returns correct values also when KeyManagerFactory is used with the OpenSSL provider. --- .../ssl/DefaultOpenSslKeyMaterial.java | 11 +- .../netty/handler/ssl/OpenSslKeyMaterial.java | 7 + .../ssl/OpenSslKeyMaterialManager.java | 3 +- .../ssl/OpenSslKeyMaterialProvider.java | 4 +- .../netty/handler/ssl/OpenSslPrivateKey.java | 16 +- .../ssl/ReferenceCountedOpenSslEngine.java | 15 +- .../netty/handler/ssl/OpenSslEngineTest.java | 14 ++ .../io/netty/handler/ssl/SSLEngineTest.java | 180 ++++++++++++++++++ 8 files changed, 240 insertions(+), 10 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java b/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java index 4bba0e741f95..fcea5266f209 100644 --- a/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java +++ b/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java @@ -22,20 +22,29 @@ import io.netty.util.ResourceLeakDetectorFactory; import io.netty.util.ResourceLeakTracker; +import java.security.cert.X509Certificate; + final class DefaultOpenSslKeyMaterial extends AbstractReferenceCounted implements OpenSslKeyMaterial { private static final ResourceLeakDetector leakDetector = ResourceLeakDetectorFactory.instance().newResourceLeakDetector(DefaultOpenSslKeyMaterial.class); private final ResourceLeakTracker leak; + private final X509Certificate[] x509CertificateChain; private long chain; private long privateKey; - DefaultOpenSslKeyMaterial(long chain, long privateKey) { + DefaultOpenSslKeyMaterial(long chain, long privateKey, X509Certificate[] x509CertificateChain) { this.chain = chain; this.privateKey = privateKey; + this.x509CertificateChain = x509CertificateChain; leak = leakDetector.track(this); } + @Override + public X509Certificate[] certificateChain() { + return x509CertificateChain.clone(); + } + @Override public long certificateChainAddress() { if (refCnt() <= 0) { diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java index 29099e5fa179..68fc85a3ed76 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java @@ -17,11 +17,18 @@ import io.netty.util.ReferenceCounted; +import java.security.cert.X509Certificate; + /** * Holds references to the native key-material that is used by OpenSSL. */ interface OpenSslKeyMaterial extends ReferenceCounted { + /** + * Returns the configured {@link X509Certificate}s. + */ + X509Certificate[] certificateChain(); + /** * Returns the pointer to the {@code STACK_OF(X509)} which holds the certificate chain. */ diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java index 94398d5cf204..9f0e0199efb2 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java @@ -96,8 +96,7 @@ private void setKeyMaterial(ReferenceCountedOpenSslEngine engine, String alias) try { keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias); if (keyMaterial != null) { - SSL.setKeyMaterial(engine.sslPointer(), - keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress()); + engine.setKeyMaterial(keyMaterial); } } catch (SSLException e) { throw e; diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java index 7430b77f7d7e..72cd2e0c8b4f 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java @@ -66,11 +66,11 @@ OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) t OpenSslKeyMaterial keyMaterial; if (key instanceof OpenSslPrivateKey) { - keyMaterial = ((OpenSslPrivateKey) key).toKeyMaterial(chain); + keyMaterial = ((OpenSslPrivateKey) key).toKeyMaterial(chain, certificates); } else { pkeyBio = toBIO(allocator, key); pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password); - keyMaterial = new DefaultOpenSslKeyMaterial(chain, pkey); + keyMaterial = new DefaultOpenSslKeyMaterial(chain, pkey, certificates); } // See the chain and pkey to 0 so we will not release it as the ownership was diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java index de1ff04daf1c..67639aae3ca6 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java @@ -18,9 +18,11 @@ import io.netty.internal.tcnative.SSL; import io.netty.util.AbstractReferenceCounted; import io.netty.util.IllegalReferenceCountException; +import io.netty.util.internal.EmptyArrays; import javax.security.auth.Destroyable; import java.security.PrivateKey; +import java.security.cert.X509Certificate; final class OpenSslPrivateKey extends AbstractReferenceCounted implements PrivateKey { @@ -110,16 +112,24 @@ public boolean isDestroyed() { /** * Convert to a {@link OpenSslKeyMaterial}. Reference count of both is shared. */ - OpenSslKeyMaterial toKeyMaterial(long certificateChain) { - return new OpenSslPrivateKeyMaterial(certificateChain); + OpenSslKeyMaterial toKeyMaterial(long certificateChain, X509Certificate[] chain) { + return new OpenSslPrivateKeyMaterial(certificateChain, chain); } private final class OpenSslPrivateKeyMaterial implements OpenSslKeyMaterial { private long certificateChain; + private final X509Certificate[] x509CertificateChain; - OpenSslPrivateKeyMaterial(long certificateChain) { + OpenSslPrivateKeyMaterial(long certificateChain, X509Certificate[] x509CertificateChain) { this.certificateChain = certificateChain; + this.x509CertificateChain = x509CertificateChain == null ? + EmptyArrays.EMPTY_X509_CERTIFICATES : x509CertificateChain; + } + + @Override + public X509Certificate[] certificateChain() { + return x509CertificateChain.clone(); } @Override diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index cf98743379ba..4537ee75f89a 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -193,6 +193,7 @@ protected void deallocate() { }; private volatile ClientAuth clientAuth = ClientAuth.NONE; + private volatile Certificate[] localCertificateChain; // Updated once a new handshake is started and so the SSLSession reused. private volatile long lastAccessed = -1; @@ -216,7 +217,6 @@ protected void deallocate() { private final OpenSslEngineMap engineMap; private final OpenSslApplicationProtocolNegotiator apn; private final OpenSslSession session; - private final Certificate[] localCerts; private final ByteBuffer[] singleSrcBuffer = new ByteBuffer[1]; private final ByteBuffer[] singleDstBuffer = new ByteBuffer[1]; private final boolean enableOcsp; @@ -323,8 +323,11 @@ public List getStatusResponses() { session = new DefaultOpenSslSession(context.sessionContext()); } engineMap = context.engineMap; - localCerts = context.keyCertChain; enableOcsp = context.enableOcsp; + // context.keyCertChain will only be non-null if we do not use the KeyManagerFactory. In this case + // localCertificateChain will be set in setKeyMaterial(...). + localCertificateChain = context.keyCertChain; + this.jdkCompatibilityMode = jdkCompatibilityMode; Lock readerLock = context.ctxLock.readLock(); readerLock.lock(); @@ -379,6 +382,11 @@ public List getStatusResponses() { leak = leakDetection ? leakDetector.track(this) : null; } + final void setKeyMaterial(OpenSslKeyMaterial keyMaterial) throws Exception { + SSL.setKeyMaterial(ssl, keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress()); + localCertificateChain = keyMaterial.certificateChain(); + } + /** * Sets the OCSP response. */ @@ -1930,6 +1938,8 @@ private final class DefaultOpenSslSession implements OpenSslSession { // thread. private X509Certificate[] x509PeerCerts; private Certificate[] peerCerts; + private Certificate[] localCerts; + private String protocol; private String cipher; private byte[] id; @@ -2070,6 +2080,7 @@ public void handshakeFinished() throws SSLException { id = SSL.getSessionId(ssl); cipher = toJavaCipherSuite(SSL.getCipherForSSL(ssl)); protocol = SSL.getVersion(ssl); + localCerts = localCertificateChain; initPeerCerts(); selectApplicationProtocol(); diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index 1ae88ebdfbe7..32be76781ad3 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -95,6 +95,20 @@ public void tearDown() throws InterruptedException { assertEquals("SSL error stack not correctly consumed", 0, SSL.getLastErrorNumber()); } + @Override + @Test + public void testSessionAfterHandshakeKeyManagerFactory() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testSessionAfterHandshakeKeyManagerFactory(); + } + + @Override + @Test + public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testSessionAfterHandshakeKeyManagerFactoryMutualAuth(); + } + @Override @Test public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth() throws Exception { diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 6e8a231c82e2..c23800162ad5 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -60,6 +60,7 @@ import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.security.KeyStore; +import java.security.Principal; import java.security.Provider; import java.security.cert.Certificate; import java.security.cert.CertificateException; @@ -82,6 +83,7 @@ import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; @@ -97,6 +99,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.verify; @@ -2726,6 +2729,183 @@ public void testGetCiphersuite() throws Exception { } } + @Test + public void testSessionAfterHandshake() throws Exception { + testSessionAfterHandshake0(false, false); + } + + @Test + public void testSessionAfterHandshakeMutualAuth() throws Exception { + testSessionAfterHandshake0(false, true); + } + + @Test + public void testSessionAfterHandshakeKeyManagerFactory() throws Exception { + testSessionAfterHandshake0(true, false); + } + + @Test + public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth() throws Exception { + testSessionAfterHandshake0(true, true); + } + + private void testSessionAfterHandshake0(boolean useKeyManagerFactory, boolean mutualAuth) throws Exception { + SelfSignedCertificate ssc = new SelfSignedCertificate(); + KeyManagerFactory kmf = useKeyManagerFactory ? + SslContext.buildKeyManagerFactory( + new java.security.cert.X509Certificate[] { ssc.cert()}, ssc.key(), null, null) : null; + + SslContextBuilder clientContextBuilder = SslContextBuilder.forClient(); + if (mutualAuth) { + if (kmf != null) { + clientContextBuilder.keyManager(kmf); + } else { + clientContextBuilder.keyManager(ssc.key(), ssc.cert()); + } + } + clientSslCtx = clientContextBuilder + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .sslContextProvider(clientSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); + + SslContextBuilder serverContextBuilder = kmf != null ? + SslContextBuilder.forServer(kmf) : + SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()); + if (mutualAuth) { + serverContextBuilder.clientAuth(ClientAuth.REQUIRE); + } + serverSslCtx = serverContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslServerProvider()) + .sslContextProvider(serverSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); + SSLEngine clientEngine = null; + SSLEngine serverEngine = null; + try { + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + + handshake(clientEngine, serverEngine); + + SSLSession clientSession = clientEngine.getSession(); + SSLSession serverSession = serverEngine.getSession(); + + assertNull(clientSession.getPeerHost()); + assertNull(serverSession.getPeerHost()); + assertEquals(-1, clientSession.getPeerPort()); + assertEquals(-1, serverSession.getPeerPort()); + + assertTrue(clientSession.getCreationTime() > 0); + assertTrue(serverSession.getCreationTime() > 0); + + assertTrue(clientSession.getLastAccessedTime() > 0); + assertTrue(serverSession.getLastAccessedTime() > 0); + + assertEquals(protocolCipherCombo.protocol, clientSession.getProtocol()); + assertEquals(protocolCipherCombo.protocol, serverSession.getProtocol()); + + assertEquals(protocolCipherCombo.cipher, clientSession.getCipherSuite()); + assertEquals(protocolCipherCombo.cipher, serverSession.getCipherSuite()); + + assertNotNull(clientSession.getId()); + assertNotNull(serverSession.getId()); + + assertTrue(clientSession.getApplicationBufferSize() > 0); + assertTrue(serverSession.getApplicationBufferSize() > 0); + + assertTrue(clientSession.getPacketBufferSize() > 0); + assertTrue(serverSession.getPacketBufferSize() > 0); + + assertNotNull(clientSession.getSessionContext()); + assertNotNull(serverSession.getSessionContext()); + + Object value = new Object(); + + assertEquals(0, clientSession.getValueNames().length); + clientSession.putValue("test", value); + assertEquals("test", clientSession.getValueNames()[0]); + assertSame(value, clientSession.getValue("test")); + clientSession.removeValue("test"); + assertEquals(0, clientSession.getValueNames().length); + + assertEquals(0, serverSession.getValueNames().length); + serverSession.putValue("test", value); + assertEquals("test", serverSession.getValueNames()[0]); + assertSame(value, serverSession.getValue("test")); + serverSession.removeValue("test"); + assertEquals(0, serverSession.getValueNames().length); + + Certificate[] serverLocalCertificates = serverSession.getLocalCertificates(); + assertEquals(1, serverLocalCertificates.length); + assertArrayEquals(ssc.cert().getEncoded(), serverLocalCertificates[0].getEncoded()); + + Principal serverLocalPrincipal = serverSession.getLocalPrincipal(); + assertNotNull(serverLocalPrincipal); + + if (mutualAuth) { + Certificate[] clientLocalCertificates = clientSession.getLocalCertificates(); + assertEquals(1, clientLocalCertificates.length); + + Certificate[] serverPeerCertificates = serverSession.getPeerCertificates(); + assertEquals(1, serverPeerCertificates.length); + assertArrayEquals(clientLocalCertificates[0].getEncoded(), serverPeerCertificates[0].getEncoded()); + + X509Certificate[] serverPeerX509Certificates = serverSession.getPeerCertificateChain(); + assertEquals(1, serverPeerX509Certificates.length); + assertArrayEquals(clientLocalCertificates[0].getEncoded(), serverPeerX509Certificates[0].getEncoded()); + + Principal clientLocalPrincipial = clientSession.getLocalPrincipal(); + assertNotNull(clientLocalPrincipial); + + Principal serverPeerPrincipal = serverSession.getPeerPrincipal(); + assertEquals(clientLocalPrincipial, serverPeerPrincipal); + } else { + assertNull(clientSession.getLocalCertificates()); + assertNull(clientSession.getLocalPrincipal()); + + try { + serverSession.getPeerCertificates(); + fail(); + } catch (SSLPeerUnverifiedException expected) { + // As we did not use mutual auth this is expected + } + + try { + serverSession.getPeerCertificateChain(); + fail(); + } catch (SSLPeerUnverifiedException expected) { + // As we did not use mutual auth this is expected + } + + try { + serverSession.getPeerPrincipal(); + fail(); + } catch (SSLPeerUnverifiedException expected) { + // As we did not use mutual auth this is expected + } + } + + Certificate[] clientPeerCertificates = clientSession.getPeerCertificates(); + assertEquals(1, clientPeerCertificates.length); + assertArrayEquals(serverLocalCertificates[0].getEncoded(), clientPeerCertificates[0].getEncoded()); + + X509Certificate[] clientPeerX509Certificates = clientSession.getPeerCertificateChain(); + assertEquals(1, clientPeerX509Certificates.length); + assertArrayEquals(serverLocalCertificates[0].getEncoded(), clientPeerX509Certificates[0].getEncoded()); + + Principal clientPeerPrincipal = clientSession.getPeerPrincipal(); + assertEquals(serverLocalPrincipal, clientPeerPrincipal); + } finally { + cleanupClientSslEngine(clientEngine); + cleanupServerSslEngine(serverEngine); + ssc.delete(); + } + } + protected SSLEngine wrapEngine(SSLEngine engine) { return engine; } From ce02d5a1848458f9f9904728d6eb1a82df62f87a Mon Sep 17 00:00:00 2001 From: JStroom Date: Fri, 16 Nov 2018 10:32:34 +0100 Subject: [PATCH 271/417] Update SslHandler.java (#8564) Swallow SSL Exception "closing inbound before receiving peer's close_notify" when running on Java 11 (#8463) Motivation: When closing a inbound SSL connection before the remote peer has send a close notify, the Java JDK is trigger happy to throw an exception. This exception can be ignored since the connection is about to be closed. The exception wasn't printed in Java 8, based on filtering on the exception message. In Java 11 the exception message has been changed. Modifications: Update the if statement to also filter/swallow the message on Java 11. Result: On Java 11 the exception isn't printed with log levels set to debug. The old behaviour is maintained. --- handler/src/main/java/io/netty/handler/ssl/SslHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index f375bc2358c5..fe533735e614 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -1570,7 +1570,8 @@ private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause, boo // // See https://github.com/netty/netty/issues/1340 String msg = e.getMessage(); - if (msg == null || !msg.contains("possible truncation attack")) { + if (msg == null || !(msg.contains("possible truncation attack") || + msg.contains("closing inbound before receiving peer's close_notify"))) { logger.debug("{} SSLEngine.closeInbound() raised an exception.", ctx.channel(), e); } } From cb0d23923f7cc05cf67942d80aa73cbe11533652 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 16 Nov 2018 10:37:29 +0100 Subject: [PATCH 272/417] Refresh DNS configuration each 5 minutes. (#8468) Motivation: We should refresh the DNS configuration each 5 minutes to be able to detect changes done by the user. This is inline with what OpenJDK is doing Modifications: Refresh config every 5 minutes. Result: Be able to consume changes made by the user. --- .../dns/DnsServerAddressStreamProviders.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java index 7765b953878b..fbcd8cc9c109 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java @@ -18,18 +18,45 @@ import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.UnstableApi; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + /** * Utility methods related to {@link DnsServerAddressStreamProvider}. */ @UnstableApi public final class DnsServerAddressStreamProviders { + // We use 5 minutes which is the same as what OpenJDK is using in sun.net.dns.ResolverConfigurationImpl. + private static final long REFRESH_INTERVAL = TimeUnit.MINUTES.toNanos(5); + // TODO(scott): how is this done on Windows? This may require a JNI call to GetNetworkParams // https://msdn.microsoft.com/en-us/library/aa365968(VS.85).aspx. private static final DnsServerAddressStreamProvider DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER = + new DnsServerAddressStreamProvider() { + private volatile DnsServerAddressStreamProvider currentProvider = provider(); + private final AtomicLong lastRefresh = new AtomicLong(System.nanoTime()); + + @Override + public DnsServerAddressStream nameServerAddressStream(String hostname) { + long last = lastRefresh.get(); + DnsServerAddressStreamProvider current = currentProvider; + if (System.nanoTime() - last > REFRESH_INTERVAL) { + // This is slightly racy which means it will be possible still use the old configuration for a small + // amount of time, but that's ok. + if (lastRefresh.compareAndSet(last, System.nanoTime())) { + current = currentProvider = provider(); + } + } + return current.nameServerAddressStream(hostname); + } + + private DnsServerAddressStreamProvider provider() { // If on windows just use the DefaultDnsServerAddressStreamProvider.INSTANCE as otherwise // we will log some error which may be confusing. - PlatformDependent.isWindows() ? DefaultDnsServerAddressStreamProvider.INSTANCE : + return PlatformDependent.isWindows() ? DefaultDnsServerAddressStreamProvider.INSTANCE : UnixResolverDnsServerAddressStreamProvider.parseSilently(); + } + }; private DnsServerAddressStreamProviders() { } From abc8a08c9662e1608e3bb495db346248836e5caf Mon Sep 17 00:00:00 2001 From: Andremoniy Date: Fri, 16 Nov 2018 17:20:28 +0100 Subject: [PATCH 273/417] Rethrow Error during retrieving remoteAddress / localAddress Motivation: Besides an error caused by closing socket in Windows a bunch of other errors may happen at this place which won't be somehow logged. For instance any VirtualMachineError as OutOfMemoryError will be simply ignored. The library should at least log the problem. Modification: Added logging of the throwable object. Result: Fixes #8499. --- transport/src/main/java/io/netty/channel/AbstractChannel.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/transport/src/main/java/io/netty/channel/AbstractChannel.java b/transport/src/main/java/io/netty/channel/AbstractChannel.java index ad671423df98..11c14a9624be 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannel.java @@ -170,6 +170,8 @@ public SocketAddress localAddress() { if (localAddress == null) { try { this.localAddress = localAddress = unsafe().localAddress(); + } catch (Error e) { + throw e; } catch (Throwable t) { // Sometimes fails on a closed socket in Windows. return null; @@ -192,6 +194,8 @@ public SocketAddress remoteAddress() { if (remoteAddress == null) { try { this.remoteAddress = remoteAddress = unsafe().remoteAddress(); + } catch (Error e) { + throw e; } catch (Throwable t) { // Sometimes fails on a closed socket in Windows. return null; From 63dc1f5aaac474f0bac60db861bfd6089d7fb688 Mon Sep 17 00:00:00 2001 From: Jake Luciani Date: Fri, 16 Nov 2018 11:22:03 -0500 Subject: [PATCH 274/417] Allow adjusting of lead detection sampling interval. (#8568) Motivation: We should allow adjustment of the leak detecting sampling interval when in SAMPLE mode. Modifications: Added new int property io.netty.leakDetection.samplingInterval Result: Be able to consume changes made by the user. --- .../main/java/io/netty/util/ResourceLeakDetector.java | 9 ++++++--- .../java/io/netty/util/ResourceLeakDetectorFactory.java | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetector.java b/common/src/main/java/io/netty/util/ResourceLeakDetector.java index a62f6df05757..436d793669ff 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetector.java +++ b/common/src/main/java/io/netty/util/ResourceLeakDetector.java @@ -48,7 +48,12 @@ public class ResourceLeakDetector { private static final String PROP_TARGET_RECORDS = "io.netty.leakDetection.targetRecords"; private static final int DEFAULT_TARGET_RECORDS = 4; + private static final String PROP_SAMPLING_INTERVAL = "io.netty.leakDetection.samplingInterval"; + // There is a minor performance benefit in TLR if this is a power of 2. + private static final int DEFAULT_SAMPLING_INTERVAL = 128; + private static final int TARGET_RECORDS; + static final int SAMPLING_INTERVAL; /** * Represents the level of resource leak detection. @@ -117,6 +122,7 @@ static Level parseLevel(String levelStr) { Level level = Level.parseLevel(levelStr); TARGET_RECORDS = SystemPropertyUtil.getInt(PROP_TARGET_RECORDS, DEFAULT_TARGET_RECORDS); + SAMPLING_INTERVAL = SystemPropertyUtil.getInt(PROP_SAMPLING_INTERVAL, DEFAULT_SAMPLING_INTERVAL); ResourceLeakDetector.level = level; if (logger.isDebugEnabled()) { @@ -125,9 +131,6 @@ static Level parseLevel(String levelStr) { } } - // There is a minor performance benefit in TLR if this is a power of 2. - static final int DEFAULT_SAMPLING_INTERVAL = 128; - /** * @deprecated Use {@link #setLevel(Level)} instead. */ diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetectorFactory.java b/common/src/main/java/io/netty/util/ResourceLeakDetectorFactory.java index 5ca63f08d304..65f1fc8c654d 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetectorFactory.java +++ b/common/src/main/java/io/netty/util/ResourceLeakDetectorFactory.java @@ -62,7 +62,7 @@ public static void setResourceLeakDetectorFactory(ResourceLeakDetectorFactory fa * @return a new instance of {@link ResourceLeakDetector} */ public final ResourceLeakDetector newResourceLeakDetector(Class resource) { - return newResourceLeakDetector(resource, ResourceLeakDetector.DEFAULT_SAMPLING_INTERVAL); + return newResourceLeakDetector(resource, ResourceLeakDetector.SAMPLING_INTERVAL); } /** @@ -90,7 +90,7 @@ public abstract ResourceLeakDetector newResourceLeakDetector( */ @SuppressWarnings("deprecation") public ResourceLeakDetector newResourceLeakDetector(Class resource, int samplingInterval) { - return newResourceLeakDetector(resource, ResourceLeakDetector.DEFAULT_SAMPLING_INTERVAL, Long.MAX_VALUE); + return newResourceLeakDetector(resource, ResourceLeakDetector.SAMPLING_INTERVAL, Long.MAX_VALUE); } /** From 278b49b2a791968c6b80ed0995ef25771b3fd654 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 19 Nov 2018 07:41:43 +0100 Subject: [PATCH 275/417] Recover from Selector IOException (#8569) Motivation: When the Selector throws an IOException during our EventLoop processing we should rebuild it and transfer the registered Channels. At the moment we will continue trying to use it which will never work. Modifications: - Rebuild Selector when an IOException is thrown during any select*(...) methods. - Add unit test. Result: Fixes https://github.com/netty/netty/issues/8566. --- .../io/netty/channel/nio/NioEventLoop.java | 10 +++- .../netty/channel/nio/NioEventLoopTest.java | 47 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index 187e1ec1a0bb..f1aed11bc462 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -401,7 +401,8 @@ private void rebuildSelector0() { protected void run() { for (;;) { try { - switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) { + try { + switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) { case SelectStrategy.CONTINUE: continue; @@ -444,6 +445,13 @@ protected void run() { } // fall through default: + } + } catch (IOException e) { + // If we receive an IOException here its because the Selector is messed up. Let's rebuild + // the selector and retry. https://github.com/netty/netty/issues/8566 + rebuildSelector0(); + handleLoopException(e); + continue; } cancelledKeys = 0; diff --git a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java index 15fcb2124426..8b176bc71c02 100644 --- a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java +++ b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java @@ -19,16 +19,22 @@ import io.netty.channel.Channel; import io.netty.channel.EventLoop; import io.netty.channel.EventLoopGroup; +import io.netty.channel.SelectStrategy; +import io.netty.channel.SelectStrategyFactory; import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.util.IntSupplier; +import io.netty.util.concurrent.DefaultThreadFactory; import io.netty.util.concurrent.Future; import org.hamcrest.core.IsInstanceOf; import org.junit.Test; +import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; import java.util.concurrent.CountDownLatch; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; @@ -211,4 +217,45 @@ public void run() { } } + @Test + public void testRebuildSelectorOnIOException() { + SelectStrategyFactory selectStrategyFactory = new SelectStrategyFactory() { + @Override + public SelectStrategy newSelectStrategy() { + return new SelectStrategy() { + + private boolean thrown; + + @Override + public int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception { + if (!thrown) { + thrown = true; + throw new IOException(); + } + return -1; + } + }; + } + }; + + EventLoopGroup group = new NioEventLoopGroup(1, new DefaultThreadFactory("ioPool"), + SelectorProvider.provider(), selectStrategyFactory); + final NioEventLoop loop = (NioEventLoop) group.next(); + try { + Channel channel = new NioServerSocketChannel(); + Selector selector = loop.unwrappedSelector(); + + loop.register(channel).syncUninterruptibly(); + + Selector newSelector = ((NioEventLoop) channel.eventLoop()).unwrappedSelector(); + assertTrue(newSelector.isOpen()); + assertNotSame(selector, newSelector); + assertFalse(selector.isOpen()); + + channel.close().syncUninterruptibly(); + } finally { + group.shutdownGracefully(); + } + } + } From 38524ec3e29100520dd4004841e6e62d7c50ba3f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 19 Nov 2018 12:13:05 +0100 Subject: [PATCH 276/417] Fix test that assumed detection of peer supported algs is not supported in BoringSSL. (#8573) Motivation: 0d2e38d5d6db6ed1abdf901230fa524c0f8db14c added supported for detection of peer supported algorithms but we missed to fix the testcase. Modifications: Fix test-case. Result: No more failing tests with BoringSSL. --- .../io/netty/handler/ssl/SniClientJava8TestUtil.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java index 26904c01179b..4db7c7b73a3e 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java @@ -64,7 +64,6 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -182,13 +181,7 @@ private static void assertSSLSession(boolean clientSide, SSLSession session, SNI if (clientSide) { Assert.assertEquals(0, extendedSSLSession.getPeerSupportedSignatureAlgorithms().length); } else { - if (session instanceof OpenSslSession && OpenSsl.isBoringSSL()) { - // BoringSSL does not support SSL_get_sigalgs(...) - // https://boringssl.googlesource.com/boringssl/+/ba16a1e405c617f4179bd780ad15522fb25b0a65%5E%21/ - Assert.assertEquals(0, extendedSSLSession.getPeerSupportedSignatureAlgorithms().length); - } else { - Assert.assertTrue(extendedSSLSession.getPeerSupportedSignatureAlgorithms().length > 0); - } + Assert.assertTrue(extendedSSLSession.getPeerSupportedSignatureAlgorithms().length >= 0); } } } From cd689ee775294178e168b0019f5c9e15272f6632 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 20 Nov 2018 16:45:15 +0100 Subject: [PATCH 277/417] Fix javadoc to correctly explain how ChannelDuplexHandler.deregister(...) works. (#8577) Motivation: We had an error in the javadoc which was most likely caused by copy and paste. Modifications: Fix javadoc. Result: Correct javadoc. --- .../src/main/java/io/netty/channel/ChannelDuplexHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java b/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java index 30faa3da846b..07c6484e5020 100644 --- a/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java +++ b/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java @@ -74,7 +74,7 @@ public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exce } /** - * Calls {@link ChannelHandlerContext#close(ChannelPromise)} to forward + * Calls {@link ChannelHandlerContext#deregister(ChannelPromise)} to forward * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. From 3d2fdc459c81ff9145b91e245d77d89cc694994f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 21 Nov 2018 06:39:28 +0100 Subject: [PATCH 278/417] Remove transitive dependency on slf4j in example (#8582) Motivation: We currently depend on slf4j in an transitive way in one of our classes in the examples. We should not do this. Modifications: Remove logging in example. Result: Remove not needed dependency. --- .../http/websocketx/server/WebSocketFrameHandler.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketFrameHandler.java b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketFrameHandler.java index aa27c0163ea4..a4536682e13e 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketFrameHandler.java +++ b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketFrameHandler.java @@ -21,16 +21,12 @@ import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Echoes uppercase content of text frames. */ public class WebSocketFrameHandler extends SimpleChannelInboundHandler { - private static final Logger logger = LoggerFactory.getLogger(WebSocketFrameHandler.class); - @Override protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { // ping and pong frames already handled @@ -38,7 +34,6 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) thr if (frame instanceof TextWebSocketFrame) { // Send the uppercase string back. String request = ((TextWebSocketFrame) frame).text(); - logger.info("{} received {}", ctx.channel(), request); ctx.channel().writeAndFlush(new TextWebSocketFrame(request.toUpperCase(Locale.US))); } else { String message = "unsupported frame type: " + frame.getClass().getName(); From d728a72e745d840da397e287ab9c59f810cdadb4 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 21 Nov 2018 06:42:40 +0100 Subject: [PATCH 279/417] Combine flushes in DnsNameResolver to allow usage of sendmmsg to reduce syscall costs (#8470) Motivation: Some of transports support gathering writes when using datagrams. For example this is the case for EpollDatagramChannel. We should minimize the calls to flush() to allow making efficient usage of sendmmsg in this case. Modifications: - minimize flush() operations when we query for multiple address types. - reduce GC by always directly schedule doResolveAll0(...) on the EventLoop. Result: Be able to use sendmmsg internally in the DnsNameResolver. --- .../netty/resolver/dns/DnsNameResolver.java | 48 ++++++++++---- .../netty/resolver/dns/DnsQueryContext.java | 18 +++-- .../netty/resolver/dns/DnsResolveContext.java | 66 +++++++++++-------- 3 files changed, 83 insertions(+), 49 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java index 5ab6c5285ed9..c8384fffe9f9 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java @@ -47,6 +47,7 @@ import io.netty.resolver.ResolvedAddressTypes; import io.netty.util.NetUtil; import io.netty.util.ReferenceCountUtil; +import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.FastThreadLocal; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; @@ -962,10 +963,32 @@ static boolean doResolveAllCached(String hostname, } } - private void doResolveAllUncached(String hostname, + private void doResolveAllUncached(final String hostname, + final DnsRecord[] additionals, + final Promise> promise, + final DnsCache resolveCache) { + // Call doResolveUncached0(...) in the EventLoop as we may need to submit multiple queries which would need + // to submit multiple Runnable at the end if we are not already on the EventLoop. + EventExecutor executor = executor(); + if (executor.inEventLoop()) { + doResolveAllUncached0(hostname, additionals, promise, resolveCache); + } else { + executor.execute(new Runnable() { + @Override + public void run() { + doResolveAllUncached0(hostname, additionals, promise, resolveCache); + } + }); + } + } + + private void doResolveAllUncached0(String hostname, DnsRecord[] additionals, Promise> promise, DnsCache resolveCache) { + + assert executor().inEventLoop(); + final DnsServerAddressStream nameServerAddrs = dnsServerAddressStreamProvider.nameServerAddressStream(hostname); new DnsAddressResolveContext(this, hostname, additionals, nameServerAddrs, @@ -1014,8 +1037,8 @@ private InetSocketAddress nextNameServerAddress() { public Future> query( InetSocketAddress nameServerAddr, DnsQuestion question) { - return query0(nameServerAddr, question, EMPTY_ADDITIONALS, - ch.eventLoop().>newPromise()); + return query0(nameServerAddr, question, EMPTY_ADDITIONALS, true, ch.newPromise(), + ch.eventLoop().>newPromise()); } /** @@ -1024,8 +1047,8 @@ public Future> query( public Future> query( InetSocketAddress nameServerAddr, DnsQuestion question, Iterable additionals) { - return query0(nameServerAddr, question, toArray(additionals, false), - ch.eventLoop().>newPromise()); + return query0(nameServerAddr, question, toArray(additionals, false), true, ch.newPromise(), + ch.eventLoop().>newPromise()); } /** @@ -1035,7 +1058,7 @@ public Future> query( InetSocketAddress nameServerAddr, DnsQuestion question, Promise> promise) { - return query0(nameServerAddr, question, EMPTY_ADDITIONALS, promise); + return query0(nameServerAddr, question, EMPTY_ADDITIONALS, true, ch.newPromise(), promise); } /** @@ -1046,7 +1069,7 @@ public Future> query( Iterable additionals, Promise> promise) { - return query0(nameServerAddr, question, toArray(additionals, false), promise); + return query0(nameServerAddr, question, toArray(additionals, false), true, ch.newPromise(), promise); } /** @@ -1067,16 +1090,14 @@ public static boolean isTimeoutError(Throwable cause) { return cause != null && cause.getCause() instanceof DnsNameResolverTimeoutException; } - final Future> query0( - InetSocketAddress nameServerAddr, DnsQuestion question, - DnsRecord[] additionals, - Promise> promise) { - return query0(nameServerAddr, question, additionals, ch.newPromise(), promise); + final void flushQueries() { + ch.flush(); } final Future> query0( InetSocketAddress nameServerAddr, DnsQuestion question, DnsRecord[] additionals, + boolean flush, ChannelPromise writePromise, Promise> promise) { assert !writePromise.isVoid(); @@ -1084,7 +1105,8 @@ final Future> query0( final Promise> castPromise = cast( checkNotNull(promise, "promise")); try { - new DnsQueryContext(this, nameServerAddr, question, additionals, castPromise).query(writePromise); + new DnsQueryContext(this, nameServerAddr, question, additionals, castPromise) + .query(flush, writePromise); return castPromise; } catch (Exception e) { return castPromise.setFailure(e); diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java index 1805b85c4b2c..08bbcb6088f7 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java @@ -89,7 +89,7 @@ DnsQuestion question() { return question; } - void query(ChannelPromise writePromise) { + void query(boolean flush, ChannelPromise writePromise) { final DnsQuestion question = question(); final InetSocketAddress nameServerAddr = nameServerAddr(); final DatagramDnsQuery query = new DatagramDnsQuery(null, nameServerAddr, id); @@ -110,18 +110,21 @@ void query(ChannelPromise writePromise) { logger.debug("{} WRITE: [{}: {}], {}", parent.ch, id, nameServerAddr, question); } - sendQuery(query, writePromise); + sendQuery(query, flush, writePromise); } - private void sendQuery(final DnsQuery query, final ChannelPromise writePromise) { + private void sendQuery(final DnsQuery query, final boolean flush, final ChannelPromise writePromise) { if (parent.channelFuture.isDone()) { - writeQuery(query, writePromise); + writeQuery(query, flush, writePromise); } else { parent.channelFuture.addListener(new GenericFutureListener>() { @Override public void operationComplete(Future future) { if (future.isSuccess()) { - writeQuery(query, writePromise); + // If the query is done in a late fashion (as the channel was not ready yet) we always flush + // to ensure we did not race with a previous flush() that was done when the Channel was not + // ready yet. + writeQuery(query, true, writePromise); } else { Throwable cause = future.cause(); promise.tryFailure(cause); @@ -132,8 +135,9 @@ public void operationComplete(Future future) { } } - private void writeQuery(final DnsQuery query, final ChannelPromise writePromise) { - final ChannelFuture writeFuture = parent.ch.writeAndFlush(query, writePromise); + private void writeQuery(final DnsQuery query, final boolean flush, final ChannelPromise writePromise) { + final ChannelFuture writeFuture = flush ? parent.ch.writeAndFlush(query, writePromise) : + parent.ch.write(query, writePromise); if (writeFuture.isDone()) { onQueryWriteCompletion(writeFuture); } else { diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java index 39d2259cdc9c..e75f49c8d46c 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java @@ -261,15 +261,20 @@ private void internalResolve(String name, Promise> promise) { name = mapping; } - DnsServerAddressStream nameServerAddressStream = getNameServers(name); + try { + DnsServerAddressStream nameServerAddressStream = getNameServers(name); - final int end = expectedTypes.length - 1; - for (int i = 0; i < end; ++i) { - if (!query(name, expectedTypes[i], nameServerAddressStream.duplicate(), promise)) { - return; + final int end = expectedTypes.length - 1; + for (int i = 0; i < end; ++i) { + if (!query(name, expectedTypes[i], nameServerAddressStream.duplicate(), false, promise)) { + return; + } } + query(name, expectedTypes[end], nameServerAddressStream, false, promise); + } finally { + // Now flush everything we submitted before. + parent.flushQueries(); } - query(name, expectedTypes[end], nameServerAddressStream, promise); } /** @@ -316,17 +321,11 @@ private DnsServerAddressStream getNameServersFromCache(String hostname) { } } - private void query(final DnsServerAddressStream nameServerAddrStream, final int nameServerAddrStreamIndex, - final DnsQuestion question, - final Promise> promise, Throwable cause) { - query(nameServerAddrStream, nameServerAddrStreamIndex, question, - parent.dnsQueryLifecycleObserverFactory().newDnsQueryLifecycleObserver(question), promise, cause); - } - private void query(final DnsServerAddressStream nameServerAddrStream, final int nameServerAddrStreamIndex, final DnsQuestion question, final DnsQueryLifecycleObserver queryLifecycleObserver, + final boolean flush, final Promise> promise, final Throwable cause) { if (nameServerAddrStreamIndex >= nameServerAddrStream.size() || allowedQueries == 0 || promise.isCancelled()) { @@ -344,9 +343,12 @@ private void query(final DnsServerAddressStream nameServerAddrStream, return; } final ChannelPromise writePromise = parent.ch.newPromise(); - final Future> f = parent.query0( - nameServerAddr, question, additionals, writePromise, - parent.ch.eventLoop().>newPromise()); + final Promise> queryPromise = + parent.ch.eventLoop().newPromise(); + + final Future> f = + parent.query0(nameServerAddr, question, additionals, flush, writePromise, queryPromise); + queriesInProgress.add(f); queryLifecycleObserver.queryWritten(nameServerAddr, writePromise); @@ -376,7 +378,8 @@ public void operationComplete(Future> future) { DnsServerAddressStream addressStream = new CombinedDnsServerAddressStream( nameServerAddr, resolvedAddresses, nameServerAddrStream); query(addressStream, nameServerAddrStreamIndex, question, - queryLifecycleObserver, promise, cause); + queryLifecycleObserver, true, promise, cause); } else { // Ignore the server and try the next one... query(nameServerAddrStream, nameServerAddrStreamIndex + 1, - question, queryLifecycleObserver, promise, cause); + question, queryLifecycleObserver, true, promise, cause); } } }); @@ -490,7 +493,7 @@ private void onResponse(final DnsServerAddressStream nameServerAddrStream, final // Retry with the next server if the server did not tell us that the domain does not exist. if (code != DnsResponseCode.NXDOMAIN) { query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, - queryLifecycleObserver.queryNoAnswer(code), promise, null); + queryLifecycleObserver.queryNoAnswer(code), true, promise, null); } else { queryLifecycleObserver.queryFailed(NXDOMAIN_QUERY_FAILED_EXCEPTION); } @@ -539,7 +542,7 @@ private boolean handleRedirect( if (serverStream != null) { query(serverStream, 0, question, queryLifecycleObserver.queryRedirected(new DnsAddressStreamList(serverStream)), - promise, null); + true, promise, null); return true; } } @@ -687,8 +690,7 @@ private void onExpectedResponse( } else { queryLifecycleObserver.querySucceed(); // We also got a CNAME so we need to ensure we also query it. - onResponseCNAME(question, cnames, - parent.dnsQueryLifecycleObserverFactory().newDnsQueryLifecycleObserver(question), promise); + onResponseCNAME(question, cnames, newDnsQueryLifecycleObserver(question), promise); } } @@ -776,10 +778,11 @@ private void tryToFinishResolve(final DnsServerAddressStream nameServerAddrStrea if (queryLifecycleObserver == NoopDnsQueryLifecycleObserver.INSTANCE) { // If the queryLifecycleObserver has already been terminated we should create a new one for this // fresh query. - query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, promise, cause); + query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, + newDnsQueryLifecycleObserver(question), true, promise, cause); } else { query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, queryLifecycleObserver, - promise, cause); + true, promise, cause); } return; } @@ -795,7 +798,7 @@ private void tryToFinishResolve(final DnsServerAddressStream nameServerAddrStrea // As the last resort, try to query CNAME, just in case the name server has it. triedCNAME = true; - query(hostname, DnsRecordType.CNAME, getNameServers(hostname), promise); + query(hostname, DnsRecordType.CNAME, getNameServers(hostname), true, promise); return; } } else { @@ -891,11 +894,12 @@ private void followCname(DnsQuestion question, String cname, DnsQueryLifecycleOb PlatformDependent.throwException(cause); return; } - query(stream, 0, cnameQuestion, queryLifecycleObserver.queryCNAMEd(cnameQuestion), promise, null); + query(stream, 0, cnameQuestion, queryLifecycleObserver.queryCNAMEd(cnameQuestion), + true, promise, null); } private boolean query(String hostname, DnsRecordType type, DnsServerAddressStream dnsServerAddressStream, - Promise> promise) { + boolean flush, Promise> promise) { final DnsQuestion question; try { question = new DefaultDnsQuestion(hostname, type, dnsClass); @@ -906,10 +910,14 @@ private boolean query(String hostname, DnsRecordType type, DnsServerAddressStrea type + ']', cause)); return false; } - query(dnsServerAddressStream, 0, question, promise, null); + query(dnsServerAddressStream, 0, question, newDnsQueryLifecycleObserver(question), flush, promise, null); return true; } + private DnsQueryLifecycleObserver newDnsQueryLifecycleObserver(DnsQuestion question) { + return parent.dnsQueryLifecycleObserverFactory().newDnsQueryLifecycleObserver(question); + } + private final class CombinedDnsServerAddressStream implements DnsServerAddressStream { private final InetSocketAddress replaced; private final DnsServerAddressStream originalStream; From 31fd66b617dd6ea5a851e80338263f2866cb4c3d Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 21 Nov 2018 15:15:01 +0100 Subject: [PATCH 280/417] Mark OIO based transports as deprecated as preparation for removal in Netty 5. (#8579) Motivation: We plan to remove the OIO based transports in Netty 5 so we should mark these as deprecated already. Modifications: Mark all OIO based transports as deprecated. Result: Give the user a heads-up for removal. --- .../main/java/io/netty/channel/sctp/oio/OioSctpChannel.java | 3 +++ .../java/io/netty/channel/sctp/oio/OioSctpServerChannel.java | 3 +++ .../src/main/java/io/netty/channel/sctp/oio/package-info.java | 3 +++ .../main/java/io/netty/channel/ThreadPerChannelEventLoop.java | 2 ++ .../java/io/netty/channel/ThreadPerChannelEventLoopGroup.java | 3 +++ .../java/io/netty/channel/oio/AbstractOioByteChannel.java | 2 ++ .../main/java/io/netty/channel/oio/AbstractOioChannel.java | 3 +++ .../java/io/netty/channel/oio/AbstractOioMessageChannel.java | 3 +++ .../main/java/io/netty/channel/oio/OioByteStreamChannel.java | 3 +++ .../src/main/java/io/netty/channel/oio/OioEventLoopGroup.java | 3 +++ .../src/main/java/io/netty/channel/oio/package-info.java | 3 +++ .../socket/oio/DefaultOioServerSocketChannelConfig.java | 3 +++ .../channel/socket/oio/DefaultOioSocketChannelConfig.java | 3 +++ .../java/io/netty/channel/socket/oio/OioDatagramChannel.java | 2 ++ .../io/netty/channel/socket/oio/OioDatagramChannelConfig.java | 4 ++++ .../io/netty/channel/socket/oio/OioServerSocketChannel.java | 3 +++ .../channel/socket/oio/OioServerSocketChannelConfig.java | 3 +++ .../java/io/netty/channel/socket/oio/OioSocketChannel.java | 3 +++ .../io/netty/channel/socket/oio/OioSocketChannelConfig.java | 3 +++ .../main/java/io/netty/channel/socket/oio/package-info.java | 3 +++ 20 files changed, 58 insertions(+) diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java index 658e0978c6e2..51a7c70f9251 100755 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java @@ -58,7 +58,10 @@ * * Be aware that not all operations systems support SCTP. Please refer to the documentation of your operation system, * to understand what you need to do to use it. Also this feature is only supported on Java 7+. + * + * @deprecated use {@link io.netty.channel.sctp.nio.NioSctpChannel}. */ +@Deprecated public class OioSctpChannel extends AbstractOioMessageChannel implements io.netty.channel.sctp.SctpChannel { diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java index 416fc9faa716..3c7372871de5 100755 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java @@ -46,7 +46,10 @@ * * Be aware that not all operations systems support SCTP. Please refer to the documentation of your operation system, * to understand what you need to do to use it. Also this feature is only supported on Java 7+. + * + * @deprecated use {@link io.netty.channel.sctp.nio.NioSctpServerChannel}. */ +@Deprecated public class OioSctpServerChannel extends AbstractOioMessageChannel implements io.netty.channel.sctp.SctpServerChannel { diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/package-info.java b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/package-info.java index a5e4de0d47f2..990faadc22a5 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/package-info.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/package-info.java @@ -17,5 +17,8 @@ /** * Old blocking I/O based SCTP channel API implementation - recommended for * a small number of connections (< 1000). + * + * @deprecated use NIO based SCTP implementation. */ +@Deprecated package io.netty.channel.sctp.oio; diff --git a/transport/src/main/java/io/netty/channel/ThreadPerChannelEventLoop.java b/transport/src/main/java/io/netty/channel/ThreadPerChannelEventLoop.java index b10d79de7eb1..1d4b95817c0b 100644 --- a/transport/src/main/java/io/netty/channel/ThreadPerChannelEventLoop.java +++ b/transport/src/main/java/io/netty/channel/ThreadPerChannelEventLoop.java @@ -19,7 +19,9 @@ * {@link SingleThreadEventLoop} which is used to handle OIO {@link Channel}'s. So in general there will be * one {@link ThreadPerChannelEventLoop} per {@link Channel}. * + * @deprecated this will be remove in the next-major release. */ +@Deprecated public class ThreadPerChannelEventLoop extends SingleThreadEventLoop { private final ThreadPerChannelEventLoopGroup parent; diff --git a/transport/src/main/java/io/netty/channel/ThreadPerChannelEventLoopGroup.java b/transport/src/main/java/io/netty/channel/ThreadPerChannelEventLoopGroup.java index f254b8de92e1..7ee89d0211d5 100644 --- a/transport/src/main/java/io/netty/channel/ThreadPerChannelEventLoopGroup.java +++ b/transport/src/main/java/io/netty/channel/ThreadPerChannelEventLoopGroup.java @@ -42,7 +42,10 @@ /** * An {@link EventLoopGroup} that creates one {@link EventLoop} per {@link Channel}. + * + * @deprecated this will be remove in the next-major release. */ +@Deprecated public class ThreadPerChannelEventLoopGroup extends AbstractEventExecutorGroup implements EventLoopGroup { private final Object[] childArgs; diff --git a/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java b/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java index 6eda848f82ef..54ea0deadd8b 100644 --- a/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java +++ b/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java @@ -34,6 +34,8 @@ /** * Abstract base class for OIO which reads and writes bytes from/to a Socket + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ public abstract class AbstractOioByteChannel extends AbstractOioChannel { diff --git a/transport/src/main/java/io/netty/channel/oio/AbstractOioChannel.java b/transport/src/main/java/io/netty/channel/oio/AbstractOioChannel.java index 7aa312d8e90b..b046d00a1c02 100644 --- a/transport/src/main/java/io/netty/channel/oio/AbstractOioChannel.java +++ b/transport/src/main/java/io/netty/channel/oio/AbstractOioChannel.java @@ -25,7 +25,10 @@ /** * Abstract base class for {@link Channel} implementations that use Old-Blocking-IO + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated public abstract class AbstractOioChannel extends AbstractChannel { protected static final int SO_TIMEOUT = 1000; diff --git a/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java b/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java index 0543b83c13ad..721631159ca2 100644 --- a/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java +++ b/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java @@ -26,7 +26,10 @@ /** * Abstract base class for OIO which reads and writes objects from/to a Socket + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated public abstract class AbstractOioMessageChannel extends AbstractOioChannel { private final List readBuf = new ArrayList(); diff --git a/transport/src/main/java/io/netty/channel/oio/OioByteStreamChannel.java b/transport/src/main/java/io/netty/channel/oio/OioByteStreamChannel.java index 6d8863b9d356..d352823fdab9 100644 --- a/transport/src/main/java/io/netty/channel/oio/OioByteStreamChannel.java +++ b/transport/src/main/java/io/netty/channel/oio/OioByteStreamChannel.java @@ -31,7 +31,10 @@ /** * Abstract base class for OIO Channels that are based on streams. + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated public abstract class OioByteStreamChannel extends AbstractOioByteChannel { private static final InputStream CLOSED_IN = new InputStream() { diff --git a/transport/src/main/java/io/netty/channel/oio/OioEventLoopGroup.java b/transport/src/main/java/io/netty/channel/oio/OioEventLoopGroup.java index 684af4098396..91a2c4b3ffbf 100644 --- a/transport/src/main/java/io/netty/channel/oio/OioEventLoopGroup.java +++ b/transport/src/main/java/io/netty/channel/oio/OioEventLoopGroup.java @@ -30,7 +30,10 @@ /** * {@link EventLoopGroup} which is used to handle OIO {@link Channel}'s. Each {@link Channel} will be handled by its * own {@link EventLoop} to not block others. + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated public class OioEventLoopGroup extends ThreadPerChannelEventLoopGroup { /** diff --git a/transport/src/main/java/io/netty/channel/oio/package-info.java b/transport/src/main/java/io/netty/channel/oio/package-info.java index 948e8492ad76..acbb62a5ba5b 100644 --- a/transport/src/main/java/io/netty/channel/oio/package-info.java +++ b/transport/src/main/java/io/netty/channel/oio/package-info.java @@ -17,5 +17,8 @@ /** * Old blocking I/O based channel API implementation - recommended for * a small number of connections (< 1000). + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated package io.netty.channel.oio; diff --git a/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioServerSocketChannelConfig.java index 05d9bb30a457..9bb9056f50a6 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioServerSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioServerSocketChannelConfig.java @@ -33,7 +33,10 @@ /** * Default {@link OioServerSocketChannelConfig} implementation + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated public class DefaultOioServerSocketChannelConfig extends DefaultServerSocketChannelConfig implements OioServerSocketChannelConfig { diff --git a/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioSocketChannelConfig.java index 3aa72fb48321..5261ebb82a52 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioSocketChannelConfig.java @@ -33,7 +33,10 @@ /** * Default {@link OioSocketChannelConfig} implementation + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated public class DefaultOioSocketChannelConfig extends DefaultSocketChannelConfig implements OioSocketChannelConfig { @Deprecated public DefaultOioSocketChannelConfig(SocketChannel channel, Socket javaSocket) { diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java index a283db74232c..17aa18f75417 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java @@ -54,7 +54,9 @@ * * @see AddressedEnvelope * @see DatagramPacket + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated public class OioDatagramChannel extends AbstractOioMessageChannel implements DatagramChannel { diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannelConfig.java index 0ebea3dbfb82..5da805e819ba 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannelConfig.java @@ -24,6 +24,10 @@ import java.net.InetAddress; import java.net.NetworkInterface; +/** + * @deprecated use NIO / EPOLL / KQUEUE transport. + */ +@Deprecated public interface OioDatagramChannelConfig extends DatagramChannelConfig { /** * Sets the maximal time a operation on the underlying socket may block. diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java index 336fcf1a6e73..bf91829b2574 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java @@ -38,7 +38,10 @@ * {@link ServerSocketChannel} which accepts new connections and create the {@link OioSocketChannel}'s for them. * * This implementation use Old-Blocking-IO. + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated public class OioServerSocketChannel extends AbstractOioMessageChannel implements ServerSocketChannel { diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannelConfig.java index 51bf6e01293b..caf3a5ebabb1 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannelConfig.java @@ -39,7 +39,10 @@ * {@link ChannelOption#SO_TIMEOUT}{@link #setSoTimeout(int)} * * + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated public interface OioServerSocketChannelConfig extends ServerSocketChannelConfig { /** diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java index 0c5580e93c29..935c316a88a8 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java @@ -39,7 +39,10 @@ /** * A {@link SocketChannel} which is using Old-Blocking-IO + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated public class OioSocketChannel extends OioByteStreamChannel implements SocketChannel { private static final InternalLogger logger = InternalLoggerFactory.getInstance(OioSocketChannel.class); diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannelConfig.java index 5b56bf179b4b..6b4574519030 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannelConfig.java @@ -39,7 +39,10 @@ * {@link ChannelOption#SO_TIMEOUT}{@link #setSoTimeout(int)} * * + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated public interface OioSocketChannelConfig extends SocketChannelConfig { /** diff --git a/transport/src/main/java/io/netty/channel/socket/oio/package-info.java b/transport/src/main/java/io/netty/channel/socket/oio/package-info.java index e73c4affb601..caf497fe331f 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/package-info.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/package-info.java @@ -17,5 +17,8 @@ /** * Old blocking I/O based socket channel API implementation - recommended for * a small number of connections (< 1000). + * + * @deprecated use NIO / EPOLL / KQUEUE transport. */ +@Deprecated package io.netty.channel.socket.oio; From 2a2bc210679d82a73cbe286a8e157cd5f4dd2449 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 23 Nov 2018 17:03:29 +0100 Subject: [PATCH 281/417] Remove @Deprecated from package-info.java file (#8591) Motivation: 31fd66b617dd6ea5a851e80338263f2866cb4c3d added @Deprecated to some classes but also to the package-info.java files. IntelliJ does not like to have these annotations on package-info.java Modifications: Remove annotation from package-info.java Result: Be able to compile against via IntelliJ --- .../src/main/java/io/netty/channel/sctp/oio/package-info.java | 1 - transport/src/main/java/io/netty/channel/oio/package-info.java | 1 - .../src/main/java/io/netty/channel/socket/oio/package-info.java | 1 - 3 files changed, 3 deletions(-) diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/package-info.java b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/package-info.java index 990faadc22a5..67e90d45d531 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/package-info.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/package-info.java @@ -20,5 +20,4 @@ * * @deprecated use NIO based SCTP implementation. */ -@Deprecated package io.netty.channel.sctp.oio; diff --git a/transport/src/main/java/io/netty/channel/oio/package-info.java b/transport/src/main/java/io/netty/channel/oio/package-info.java index acbb62a5ba5b..af5b19a1b387 100644 --- a/transport/src/main/java/io/netty/channel/oio/package-info.java +++ b/transport/src/main/java/io/netty/channel/oio/package-info.java @@ -20,5 +20,4 @@ * * @deprecated use NIO / EPOLL / KQUEUE transport. */ -@Deprecated package io.netty.channel.oio; diff --git a/transport/src/main/java/io/netty/channel/socket/oio/package-info.java b/transport/src/main/java/io/netty/channel/socket/oio/package-info.java index caf497fe331f..786ca08556ec 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/package-info.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/package-info.java @@ -20,5 +20,4 @@ * * @deprecated use NIO / EPOLL / KQUEUE transport. */ -@Deprecated package io.netty.channel.socket.oio; From af34287fd11887db05b01c06a16a99e91560fee1 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 24 Nov 2018 10:47:56 +0100 Subject: [PATCH 282/417] HeadContext is inbound and outbound (#8592) Motivation: Our HeadContext in DefaultChannelPipeline does handle inbound and outbound but we only marked it as outbound. While this does not have any effect in the current code-base it can lead to problems when we change our internals (this is also how I found the bug). Modifications: Construct HeadContext so it is also marked as handling inbound. Result: More correct code. --- .../src/main/java/io/netty/channel/DefaultChannelPipeline.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java index 0d3307a5351a..8a6f6dae4665 100644 --- a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java @@ -1331,7 +1331,7 @@ final class HeadContext extends AbstractChannelHandlerContext private final Unsafe unsafe; HeadContext(DefaultChannelPipeline pipeline) { - super(pipeline, null, HEAD_NAME, false, true); + super(pipeline, null, HEAD_NAME, true, true); unsafe = pipeline.channel().unsafe(); setAddComplete(); } From af636267772da6dcde43618b20c5e1be8e418d5f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 25 Nov 2018 21:46:14 +0100 Subject: [PATCH 283/417] Factor out less common code-path into own method to allow inlining. (#8590) Motivation: During benchmarks two methods showed up as "hot method too big". We can easily make these smaller by factor out some less common code-path to an extra method and so allow inlining. Modifications: Factor out less common code path to an extra method. Result: Hot methods can be inlined. --- .../netty/channel/ChannelOutboundBuffer.java | 37 +++++++++++-------- .../io/netty/channel/nio/NioEventLoop.java | 29 +++++++++------ 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java b/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java index 155d73f35eb3..d3a934a82973 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java +++ b/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java @@ -434,21 +434,9 @@ public ByteBuffer[] nioBuffers(int maxCount, long maxBytes) { } nioBuffers[nioBufferCount++] = nioBuf; } else { - ByteBuffer[] nioBufs = entry.bufs; - if (nioBufs == null) { - // cached ByteBuffers as they may be expensive to create in terms - // of Object allocation - entry.bufs = nioBufs = buf.nioBuffers(); - } - for (int i = 0; i < nioBufs.length && nioBufferCount < maxCount; ++i) { - ByteBuffer nioBuf = nioBufs[i]; - if (nioBuf == null) { - break; - } else if (!nioBuf.hasRemaining()) { - continue; - } - nioBuffers[nioBufferCount++] = nioBuf; - } + // The code exists in an extra method to ensure the method is not too big to inline as this + // branch is not very likely to get hit very frequently. + nioBufferCount = nioBuffers(entry, buf, nioBuffers, nioBufferCount, maxCount); } if (nioBufferCount == maxCount) { break; @@ -463,6 +451,25 @@ public ByteBuffer[] nioBuffers(int maxCount, long maxBytes) { return nioBuffers; } + private static int nioBuffers(Entry entry, ByteBuf buf, ByteBuffer[] nioBuffers, int nioBufferCount, int maxCount) { + ByteBuffer[] nioBufs = entry.bufs; + if (nioBufs == null) { + // cached ByteBuffers as they may be expensive to create in terms + // of Object allocation + entry.bufs = nioBufs = buf.nioBuffers(); + } + for (int i = 0; i < nioBufs.length && nioBufferCount < maxCount; ++i) { + ByteBuffer nioBuf = nioBufs[i]; + if (nioBuf == null) { + break; + } else if (!nioBuf.hasRemaining()) { + continue; + } + nioBuffers[nioBufferCount++] = nioBuf; + } + return nioBufferCount; + } + private static ByteBuffer[] expandNioBufferArray(ByteBuffer[] array, int neededSpace, int size) { int newCapacity = array.length; do { diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index f1aed11bc462..539ac81d37d0 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -793,17 +793,9 @@ private void select(boolean oldWakenUp) throws IOException { selectCnt = 1; } else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) { - // The selector returned prematurely many times in a row. - // Rebuild the selector to work around the problem. - logger.warn( - "Selector.select() returned prematurely {} times in a row; rebuilding Selector {}.", - selectCnt, selector); - - rebuildSelector(); - selector = this.selector; - - // Select again to populate selectedKeys. - selector.selectNow(); + // The code exists in an extra method to ensure the method is not too big to inline as this + // branch is not very likely to get hit very frequently. + selector = selectRebuildSelector(selectCnt); selectCnt = 1; break; } @@ -826,6 +818,21 @@ private void select(boolean oldWakenUp) throws IOException { } } + private Selector selectRebuildSelector(int selectCnt) throws IOException { + // The selector returned prematurely many times in a row. + // Rebuild the selector to work around the problem. + logger.warn( + "Selector.select() returned prematurely {} times in a row; rebuilding Selector {}.", + selectCnt, selector); + + rebuildSelector(); + Selector selector = this.selector; + + // Select again to populate selectedKeys. + selector.selectNow(); + return selector; + } + private void selectAgain() { needsToSelectAgain = false; try { From 2278991db7024b075972c0ec7a211a1fcc931450 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 27 Nov 2018 08:33:28 +0100 Subject: [PATCH 284/417] Use addAndGet(...) as a replacement for compareAndSet(...) when tracking the direct memory usage. (#8596) Motivation: We can change from using compareAndSet to addAndGet, which emits a different CPU instruction on x86 (CMPXCHG to XADD) when count direct memory usage. This instruction is cheaper in general and so produce less overhead on the "happy path". If we detect too much memory usage we just rollback the change before throwing the Error. Modifications: Replace compareAndSet(...) with addAndGet(...) Result: Less overhead when tracking direct memory. --- .../netty/util/internal/PlatformDependent.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index d60c6e76e701..1baeecbf7c90 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -649,16 +649,12 @@ public static void freeDirectNoCleaner(ByteBuffer buffer) { private static void incrementMemoryCounter(int capacity) { if (DIRECT_MEMORY_COUNTER != null) { - for (;;) { - long usedMemory = DIRECT_MEMORY_COUNTER.get(); - long newUsedMemory = usedMemory + capacity; - if (newUsedMemory > DIRECT_MEMORY_LIMIT) { - throw new OutOfDirectMemoryError("failed to allocate " + capacity - + " byte(s) of direct memory (used: " + usedMemory + ", max: " + DIRECT_MEMORY_LIMIT + ')'); - } - if (DIRECT_MEMORY_COUNTER.compareAndSet(usedMemory, newUsedMemory)) { - break; - } + long newUsedMemory = DIRECT_MEMORY_COUNTER.addAndGet(capacity); + if (newUsedMemory > DIRECT_MEMORY_LIMIT) { + DIRECT_MEMORY_COUNTER.addAndGet(-capacity); + throw new OutOfDirectMemoryError("failed to allocate " + capacity + + " byte(s) of direct memory (used: " + (newUsedMemory - capacity) + + ", max: " + DIRECT_MEMORY_LIMIT + ')'); } } } From f4e4147df85b4684cbc8800a8be5ff0fe8bfc58e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 27 Nov 2018 11:44:27 +0100 Subject: [PATCH 285/417] LocationAwareSlf4jLogger does not correctly format log message. (#8595) Motivation: We did miss to use MessageFormatter inside LocationAwareSlf4jLogger and so {} was not correctly replaced in log messages when using slf4j. This regression was introduced by afe0767e9c150369cd6ea150b5cc1a9021fd5c3c. Modifications: - Make use of MessageFormatter - Add unit test. Result: Fixes https://github.com/netty/netty/issues/8483. --- .../logging/LocationAwareSlf4JLogger.java | 46 +++++++------ .../logging/Slf4JLoggerFactoryTest.java | 67 ++++++++++++++++++- 2 files changed, 90 insertions(+), 23 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java b/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java index 13073b02b418..33eb705a8f0a 100644 --- a/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java +++ b/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java @@ -28,7 +28,7 @@ final class LocationAwareSlf4JLogger extends AbstractInternalLogger { // IMPORTANT: All our log methods first check if the log level is enabled before call the wrapped // LocationAwareLogger.log(...) method. This is done to reduce GC creation that is caused by varargs. - private static final String FQCN = LocationAwareSlf4JLogger.class.getName(); + static final String FQCN = LocationAwareSlf4JLogger.class.getName(); private static final long serialVersionUID = -8292030083201538180L; private final transient LocationAwareLogger logger; @@ -38,12 +38,16 @@ final class LocationAwareSlf4JLogger extends AbstractInternalLogger { this.logger = logger; } - private void log(final int level, final String message, final Object... params) { - logger.log(null, FQCN, level, message, params, null); + private void log(final int level, final String message) { + logger.log(null, FQCN, level, message, null, null); } - private void log(final int level, final String message, Throwable throwable, final Object... params) { - logger.log(null, FQCN, level, message, params, throwable); + private void log(final int level, final String message, Throwable cause) { + logger.log(null, FQCN, level, message, null, cause); + } + + private void log(final int level, final org.slf4j.helpers.FormattingTuple tuple) { + logger.log(null, FQCN, level, tuple.getMessage(), tuple.getArgArray(), tuple.getThrowable()); } @Override @@ -54,28 +58,28 @@ public boolean isTraceEnabled() { @Override public void trace(String msg) { if (isTraceEnabled()) { - log(TRACE_INT, msg, null); + log(TRACE_INT, msg); } } @Override public void trace(String format, Object arg) { if (isTraceEnabled()) { - log(TRACE_INT, format, arg); + log(TRACE_INT, org.slf4j.helpers.MessageFormatter.format(format, arg)); } } @Override public void trace(String format, Object argA, Object argB) { if (isTraceEnabled()) { - log(TRACE_INT, format, argA, argB); + log(TRACE_INT, org.slf4j.helpers.MessageFormatter.format(format, argA, argB)); } } @Override public void trace(String format, Object... argArray) { if (isTraceEnabled()) { - log(TRACE_INT, format, argArray); + log(TRACE_INT, org.slf4j.helpers.MessageFormatter.format(format, argArray)); } } @@ -101,21 +105,21 @@ public void debug(String msg) { @Override public void debug(String format, Object arg) { if (isDebugEnabled()) { - log(DEBUG_INT, format, arg); + log(DEBUG_INT, org.slf4j.helpers.MessageFormatter.format(format, arg)); } } @Override public void debug(String format, Object argA, Object argB) { if (isDebugEnabled()) { - log(DEBUG_INT, format, argA, argB); + log(DEBUG_INT, org.slf4j.helpers.MessageFormatter.format(format, argA, argB)); } } @Override public void debug(String format, Object... argArray) { if (isDebugEnabled()) { - log(DEBUG_INT, format, argArray); + log(DEBUG_INT, org.slf4j.helpers.MessageFormatter.format(format, argArray)); } } @@ -141,21 +145,21 @@ public void info(String msg) { @Override public void info(String format, Object arg) { if (isInfoEnabled()) { - log(INFO_INT, format, arg); + log(INFO_INT, org.slf4j.helpers.MessageFormatter.format(format, arg)); } } @Override public void info(String format, Object argA, Object argB) { if (isInfoEnabled()) { - log(INFO_INT, format, argA, argB); + log(INFO_INT, org.slf4j.helpers.MessageFormatter.format(format, argA, argB)); } } @Override public void info(String format, Object... argArray) { if (isInfoEnabled()) { - log(INFO_INT, format, argArray); + log(INFO_INT, org.slf4j.helpers.MessageFormatter.format(format, argArray)); } } @@ -181,21 +185,21 @@ public void warn(String msg) { @Override public void warn(String format, Object arg) { if (isWarnEnabled()) { - log(WARN_INT, format, arg); + log(WARN_INT, org.slf4j.helpers.MessageFormatter.format(format, arg)); } } @Override public void warn(String format, Object... argArray) { if (isWarnEnabled()) { - log(WARN_INT, format, argArray); + log(WARN_INT, org.slf4j.helpers.MessageFormatter.format(format, argArray)); } } @Override public void warn(String format, Object argA, Object argB) { if (isWarnEnabled()) { - log(WARN_INT, format, argA, argB); + log(WARN_INT, org.slf4j.helpers.MessageFormatter.format(format, argA, argB)); } } @@ -221,21 +225,21 @@ public void error(String msg) { @Override public void error(String format, Object arg) { if (isErrorEnabled()) { - log(ERROR_INT, format, arg); + log(ERROR_INT, org.slf4j.helpers.MessageFormatter.format(format, arg)); } } @Override public void error(String format, Object argA, Object argB) { if (isErrorEnabled()) { - log(ERROR_INT, format, argA, argB); + log(ERROR_INT, org.slf4j.helpers.MessageFormatter.format(format, argA, argB)); } } @Override public void error(String format, Object... argArray) { if (isErrorEnabled()) { - log(ERROR_INT, format, argArray); + log(ERROR_INT, org.slf4j.helpers.MessageFormatter.format(format, argArray)); } } diff --git a/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java b/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java index 6b3cd8501589..8cb2f84f7a90 100644 --- a/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java +++ b/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java @@ -16,13 +16,19 @@ package io.netty.util.internal.logging; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.slf4j.Logger; +import org.slf4j.Marker; import org.slf4j.spi.LocationAwareLogger; +import java.util.Iterator; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; public class Slf4JLoggerFactoryTest { @@ -50,4 +56,61 @@ public void testCreationLocationAwareLogger() { assertTrue(internalLogger instanceof LocationAwareSlf4JLogger); assertEquals("testlogger", internalLogger.name()); } + + @Test + public void testFormatMessage() { + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + LocationAwareLogger logger = mock(LocationAwareLogger.class); + when(logger.isDebugEnabled()).thenReturn(true); + when(logger.isErrorEnabled()).thenReturn(true); + when(logger.isInfoEnabled()).thenReturn(true); + when(logger.isTraceEnabled()).thenReturn(true); + when(logger.isWarnEnabled()).thenReturn(true); + when(logger.getName()).thenReturn("testlogger"); + + InternalLogger internalLogger = Slf4JLoggerFactory.wrapLogger(logger); + internalLogger.debug("{}", "debug"); + internalLogger.debug("{} {}", "debug1", "debug2"); + + internalLogger.error("{}", "error"); + internalLogger.error("{} {}", "error1", "error2"); + + internalLogger.info("{}", "info"); + internalLogger.info("{} {}", "info1", "info2"); + + internalLogger.trace("{}", "trace"); + internalLogger.trace("{} {}", "trace1", "trace2"); + + internalLogger.warn("{}", "warn"); + internalLogger.warn("{} {}", "warn1", "warn2"); + + verify(logger, times(2)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), + eq(LocationAwareLogger.DEBUG_INT), captor.capture(), any(Object[].class), + ArgumentMatchers.isNull()); + verify(logger, times(2)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), + eq(LocationAwareLogger.ERROR_INT), captor.capture(), any(Object[].class), + ArgumentMatchers.isNull()); + verify(logger, times(2)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), + eq(LocationAwareLogger.INFO_INT), captor.capture(), any(Object[].class), + ArgumentMatchers.isNull()); + verify(logger, times(2)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), + eq(LocationAwareLogger.TRACE_INT), captor.capture(), any(Object[].class), + ArgumentMatchers.isNull()); + verify(logger, times(2)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), + eq(LocationAwareLogger.WARN_INT), captor.capture(), any(Object[].class), + ArgumentMatchers.isNull()); + + Iterator logMessages = captor.getAllValues().iterator(); + assertEquals("debug", logMessages.next()); + assertEquals("debug1 debug2", logMessages.next()); + assertEquals("error", logMessages.next()); + assertEquals("error1 error2", logMessages.next()); + assertEquals("info", logMessages.next()); + assertEquals("info1 info2", logMessages.next()); + assertEquals("trace", logMessages.next()); + assertEquals("trace1 trace2", logMessages.next()); + assertEquals("warn", logMessages.next()); + assertEquals("warn1 warn2", logMessages.next()); + assertFalse(logMessages.hasNext()); + } } From 89639ce322182dd83206fc3c0b7caddc53413cc9 Mon Sep 17 00:00:00 2001 From: Rolandz Date: Tue, 27 Nov 2018 18:47:34 +0800 Subject: [PATCH 286/417] Fix offset calculation in PooledByteBufAllocator when used Motivation: When we create new chunk with memory aligned, the offset of direct memory should be 'alignment - address & (alignment - 1)', not just 'address & (alignment - 1)'. Modification: Change offset calculating formula to offset = alignment - address & (alignment - 1) in PoolArena.DirectArena#offsetCacheLine and add a unit test to assert that. Result: Correctly calculate offset. --- .../main/java/io/netty/buffer/PoolArena.java | 11 +++++++--- .../java/io/netty/buffer/PoolArenaTest.java | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/PoolArena.java b/buffer/src/main/java/io/netty/buffer/PoolArena.java index 465315b67449..08d7a38e91e2 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolArena.java +++ b/buffer/src/main/java/io/netty/buffer/PoolArena.java @@ -725,11 +725,16 @@ boolean isDirect() { return true; } - private int offsetCacheLine(ByteBuffer memory) { + // mark as package-private, only for unit test + int offsetCacheLine(ByteBuffer memory) { // We can only calculate the offset if Unsafe is present as otherwise directBufferAddress(...) will // throw an NPE. - return HAS_UNSAFE ? - (int) (PlatformDependent.directBufferAddress(memory) & directMemoryCacheAlignmentMask) : 0; + int remainder = HAS_UNSAFE + ? (int) (PlatformDependent.directBufferAddress(memory) & directMemoryCacheAlignmentMask) + : 0; + + // offset = alignment - address & (alignment - 1) + return directMemoryCacheAlignment - remainder; } @Override diff --git a/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java b/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java index 3fafef9c04ef..2c1bd12db32b 100644 --- a/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java +++ b/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java @@ -16,6 +16,7 @@ package io.netty.buffer; +import io.netty.util.internal.PlatformDependent; import org.junit.Assert; import org.junit.Test; @@ -43,6 +44,25 @@ public void testNormalizeAlignedCapacity() throws Exception { } } + @Test + public void testDirectArenaOffsetCacheLine() throws Exception { + int capacity = 5; + int alignment = 128; + + for (int i = 0; i < 1000; i++) { + ByteBuffer bb = PlatformDependent.useDirectBufferNoCleaner() + ? PlatformDependent.allocateDirectNoCleaner(capacity + alignment) + : ByteBuffer.allocateDirect(capacity + alignment); + + PoolArena.DirectArena arena = new PoolArena.DirectArena(null, 0, 0, 9, 9, alignment); + int offset = arena.offsetCacheLine(bb); + long address = PlatformDependent.directBufferAddress(bb); + + Assert.assertEquals(0, (offset + address) & (alignment - 1)); + PlatformDependent.freeDirectBuffer(bb); + } + } + @Test public final void testAllocationCounter() { final PooledByteBufAllocator allocator = new PooledByteBufAllocator( From 8cd005ba4346fd249a3828703fc4cfa64489bff3 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 27 Nov 2018 13:55:13 +0100 Subject: [PATCH 287/417] Provide a way to cache the internal nioBuffer of the PooledByteBuffer to reduce GC. (#8593) Motivation: Often a temporary ByteBuffer is used which can be cached to reduce the GC pressure. Modifications: Add a Deque per PoolChunk which will be used for caching. Result: Less GC. --- .../main/java/io/netty/buffer/PoolChunk.java | 28 ++++++++++++++++++- .../java/io/netty/buffer/PooledByteBuf.java | 8 ++++-- .../netty/buffer/PooledByteBufAllocator.java | 6 ++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunk.java b/buffer/src/main/java/io/netty/buffer/PoolChunk.java index 93eda84104bf..d4b952ffda59 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolChunk.java +++ b/buffer/src/main/java/io/netty/buffer/PoolChunk.java @@ -16,6 +16,10 @@ package io.netty.buffer; +import java.nio.ByteBuffer; +import java.util.ArrayDeque; +import java.util.Deque; + /** * Description of algorithm for PageRun/PoolSubpage allocation from PoolChunk * @@ -107,7 +111,6 @@ final class PoolChunk implements PoolChunkMetric { final T memory; final boolean unpooled; final int offset; - private final byte[] memoryMap; private final byte[] depthMap; private final PoolSubpage[] subpages; @@ -122,6 +125,13 @@ final class PoolChunk implements PoolChunkMetric { /** Used to mark memory as unusable */ private final byte unusable; + // Use as cache for ByteBuffer created from the memory. These are just duplicates and so are only a container + // around the memory itself. These are often needed for operations within the Pooled*DirectByteBuf and so + // may produce extra GC, which can be greatly reduced by caching the duplicates. + // + // This may be null if the PoolChunk is unpooled as pooling the ByteBuffer instances does not make any sense here. + private final Deque cachedNioBuffers; + private int freeBytes; PoolChunkList parent; @@ -163,6 +173,7 @@ final class PoolChunk implements PoolChunkMetric { } subpages = newSubpageArray(maxSubpageAllocs); + cachedNioBuffers = new ArrayDeque(); } /** Creates a special chunk that is not pooled. */ @@ -182,6 +193,21 @@ final class PoolChunk implements PoolChunkMetric { chunkSize = size; log2ChunkSize = log2(chunkSize); maxSubpageAllocs = 0; + cachedNioBuffers = null; + } + + ByteBuffer pollCachedNioBuffer() { + // We use LIFO to increase the chance that its still "hot" and so in the CPU / L* cache. + return cachedNioBuffers != null ? cachedNioBuffers.pollLast() : null; + } + + void offerCachedNioBuffer(ByteBuffer nioBuffer) { + // Only cache if we did not reach the limit yet and if its not unpooled. + // If we do just drop it on the floor and let the GC collect it. + if (cachedNioBuffers != null && + cachedNioBuffers.size() <= PooledByteBufAllocator.DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK) { + cachedNioBuffers.offer(nioBuffer); + } } @SuppressWarnings("unchecked") diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java index 56a4be387232..699ca1ca627f 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java @@ -56,13 +56,13 @@ private void init0(PoolChunk chunk, long handle, int offset, int length, int this.chunk = chunk; memory = chunk.memory; + tmpNioBuf = chunk.pollCachedNioBuffer(); allocator = chunk.arena.parent; this.cache = cache; this.handle = handle; this.offset = offset; this.length = length; this.maxLength = maxLength; - tmpNioBuf = null; } /** @@ -166,7 +166,11 @@ protected final void deallocate() { final long handle = this.handle; this.handle = -1; memory = null; - tmpNioBuf = null; + if (tmpNioBuf != null) { + // Try to put back into the cache for later usage. + chunk.offerCachedNioBuffer(tmpNioBuf); + tmpNioBuf = null; + } chunk.arena.free(chunk, handle, maxLength, cache); chunk = null; recycle(); diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java index aa9ee28b9484..92bb476fe756 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java @@ -45,6 +45,7 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements private static final int DEFAULT_CACHE_TRIM_INTERVAL; private static final boolean DEFAULT_USE_CACHE_FOR_ALL_THREADS; private static final int DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT; + static final int DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK; private static final int MIN_PAGE_SIZE = 4096; private static final int MAX_CHUNK_SIZE = (int) (((long) Integer.MAX_VALUE + 1) / 2); @@ -116,6 +117,9 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = SystemPropertyUtil.getInt( "io.netty.allocator.directMemoryCacheAlignment", 0); + DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK = SystemPropertyUtil.getInt( + "io.netty.allocator.maxCachedByteBuffersPerChunk", 1024); + if (logger.isDebugEnabled()) { logger.debug("-Dio.netty.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA); logger.debug("-Dio.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA); @@ -136,6 +140,8 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements logger.debug("-Dio.netty.allocator.maxCachedBufferCapacity: {}", DEFAULT_MAX_CACHED_BUFFER_CAPACITY); logger.debug("-Dio.netty.allocator.cacheTrimInterval: {}", DEFAULT_CACHE_TRIM_INTERVAL); logger.debug("-Dio.netty.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS); + logger.debug("-Dio.netty.allocator.maxCachedByteBuffersPerChunk: {}", + DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK); } } From 15e4fe05a81aaec830c8502cb5ec6737526636d2 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 27 Nov 2018 20:02:34 +0100 Subject: [PATCH 288/417] Revert "Provide a way to cache the internal nioBuffer of the PooledByteBuffer to reduce GC. (#8593)" This reverts commit 8cd005ba4346fd249a3828703fc4cfa64489bff3 as it seems to produce some failures in some cases. This needs more research. --- .../main/java/io/netty/buffer/PoolChunk.java | 28 +------------------ .../java/io/netty/buffer/PooledByteBuf.java | 8 ++---- .../netty/buffer/PooledByteBufAllocator.java | 6 ---- 3 files changed, 3 insertions(+), 39 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunk.java b/buffer/src/main/java/io/netty/buffer/PoolChunk.java index d4b952ffda59..93eda84104bf 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolChunk.java +++ b/buffer/src/main/java/io/netty/buffer/PoolChunk.java @@ -16,10 +16,6 @@ package io.netty.buffer; -import java.nio.ByteBuffer; -import java.util.ArrayDeque; -import java.util.Deque; - /** * Description of algorithm for PageRun/PoolSubpage allocation from PoolChunk * @@ -111,6 +107,7 @@ final class PoolChunk implements PoolChunkMetric { final T memory; final boolean unpooled; final int offset; + private final byte[] memoryMap; private final byte[] depthMap; private final PoolSubpage[] subpages; @@ -125,13 +122,6 @@ final class PoolChunk implements PoolChunkMetric { /** Used to mark memory as unusable */ private final byte unusable; - // Use as cache for ByteBuffer created from the memory. These are just duplicates and so are only a container - // around the memory itself. These are often needed for operations within the Pooled*DirectByteBuf and so - // may produce extra GC, which can be greatly reduced by caching the duplicates. - // - // This may be null if the PoolChunk is unpooled as pooling the ByteBuffer instances does not make any sense here. - private final Deque cachedNioBuffers; - private int freeBytes; PoolChunkList parent; @@ -173,7 +163,6 @@ final class PoolChunk implements PoolChunkMetric { } subpages = newSubpageArray(maxSubpageAllocs); - cachedNioBuffers = new ArrayDeque(); } /** Creates a special chunk that is not pooled. */ @@ -193,21 +182,6 @@ final class PoolChunk implements PoolChunkMetric { chunkSize = size; log2ChunkSize = log2(chunkSize); maxSubpageAllocs = 0; - cachedNioBuffers = null; - } - - ByteBuffer pollCachedNioBuffer() { - // We use LIFO to increase the chance that its still "hot" and so in the CPU / L* cache. - return cachedNioBuffers != null ? cachedNioBuffers.pollLast() : null; - } - - void offerCachedNioBuffer(ByteBuffer nioBuffer) { - // Only cache if we did not reach the limit yet and if its not unpooled. - // If we do just drop it on the floor and let the GC collect it. - if (cachedNioBuffers != null && - cachedNioBuffers.size() <= PooledByteBufAllocator.DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK) { - cachedNioBuffers.offer(nioBuffer); - } } @SuppressWarnings("unchecked") diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java index 699ca1ca627f..56a4be387232 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java @@ -56,13 +56,13 @@ private void init0(PoolChunk chunk, long handle, int offset, int length, int this.chunk = chunk; memory = chunk.memory; - tmpNioBuf = chunk.pollCachedNioBuffer(); allocator = chunk.arena.parent; this.cache = cache; this.handle = handle; this.offset = offset; this.length = length; this.maxLength = maxLength; + tmpNioBuf = null; } /** @@ -166,11 +166,7 @@ protected final void deallocate() { final long handle = this.handle; this.handle = -1; memory = null; - if (tmpNioBuf != null) { - // Try to put back into the cache for later usage. - chunk.offerCachedNioBuffer(tmpNioBuf); - tmpNioBuf = null; - } + tmpNioBuf = null; chunk.arena.free(chunk, handle, maxLength, cache); chunk = null; recycle(); diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java index 92bb476fe756..aa9ee28b9484 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java @@ -45,7 +45,6 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements private static final int DEFAULT_CACHE_TRIM_INTERVAL; private static final boolean DEFAULT_USE_CACHE_FOR_ALL_THREADS; private static final int DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT; - static final int DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK; private static final int MIN_PAGE_SIZE = 4096; private static final int MAX_CHUNK_SIZE = (int) (((long) Integer.MAX_VALUE + 1) / 2); @@ -117,9 +116,6 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = SystemPropertyUtil.getInt( "io.netty.allocator.directMemoryCacheAlignment", 0); - DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK = SystemPropertyUtil.getInt( - "io.netty.allocator.maxCachedByteBuffersPerChunk", 1024); - if (logger.isDebugEnabled()) { logger.debug("-Dio.netty.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA); logger.debug("-Dio.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA); @@ -140,8 +136,6 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements logger.debug("-Dio.netty.allocator.maxCachedBufferCapacity: {}", DEFAULT_MAX_CACHED_BUFFER_CAPACITY); logger.debug("-Dio.netty.allocator.cacheTrimInterval: {}", DEFAULT_CACHE_TRIM_INTERVAL); logger.debug("-Dio.netty.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS); - logger.debug("-Dio.netty.allocator.maxCachedByteBuffersPerChunk: {}", - DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK); } } From 057c19f92ac8b5f9ee34b9e6d813a65ee4707c15 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 27 Nov 2018 21:03:35 +0100 Subject: [PATCH 289/417] Move less common code-path to extra method to allow inlining of writeUtf8. (#8600) Motivation: ByteBuf is used everywhere so we should try hard to be able to make things inlinable. During benchmarks it showed that writeCharSequence(...) fails to inline writeUtf8 because it is too big even if its hots. Modifications: Move less common code-path to extra method to allow inlining. Result: Be able to inline writeUtf8 in most cases. --- .../java/io/netty/buffer/ByteBufUtil.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index 6cd9b211ebf1..d2b2ec334f98 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -557,17 +557,8 @@ static int writeUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); break; } - if (!Character.isLowSurrogate(c2)) { - buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); - buffer._setByte(writerIndex++, Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2); - continue; - } - int codePoint = Character.toCodePoint(c, c2); - // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. - buffer._setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18))); - buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f))); - buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f))); - buffer._setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f))); + // Extra method to allow inlining the rest of writeUtf8 which is the most likely code path. + writerIndex = writeUtf8Surrogate(buffer, writerIndex, c, c2); } else { buffer._setByte(writerIndex++, (byte) (0xe0 | (c >> 12))); buffer._setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f))); @@ -577,6 +568,21 @@ static int writeUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, return writerIndex - oldWriterIndex; } + private static int writeUtf8Surrogate(AbstractByteBuf buffer, int writerIndex, char c, char c2) { + if (!Character.isLowSurrogate(c2)) { + buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); + buffer._setByte(writerIndex++, Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2); + return writerIndex; + } + int codePoint = Character.toCodePoint(c, c2); + // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. + buffer._setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18))); + buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f))); + buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f))); + buffer._setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f))); + return writerIndex; + } + /** * Returns max bytes length of UTF8 character sequence of the given length. */ From fedf3ccecb80ddccd05da2d02e76d669fe2c219c Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Wed, 28 Nov 2018 23:32:32 -0800 Subject: [PATCH 290/417] Harden ref-counting concurrency semantics (#8583) Motivation #8563 highlighted race conditions introduced by the prior optimistic update optimization in 83a19d565064ee36998eb94f946e5a4264001065. These were known at the time but considered acceptable given the perf benefit in high contention scenarios. This PR proposes a modified approach which provides roughly half the gains but stronger concurrency semantics. Race conditions still exist but their scope is narrowed to much less likely cases (releases coinciding with retain overflow), and even in those cases certain guarantees are still assured. Once release() returns true, all subsequent release/retains are guaranteed to throw, and in particular deallocate will be called at most once. Modifications - Use even numbers internally (including -ve) for live refcounts - "Final" release changes to odd number (equivalent to refcount 0) - Retain still uses faster getAndAdd, release uses CAS loop - First CAS attempt uses non-volatile read - Thread.yield() after a failed CAS provides a net gain Result More (though not completely) robust concurrency semantics for ref counting; increased latency under high contention, but still roughly twice as fast as the original logic. Bench results to follow --- .../AbstractReferenceCountedByteBuf.java | 98 +++++++++++---- .../netty/util/AbstractReferenceCounted.java | 118 ++++++++++++++---- .../util/AbstractReferenceCountedTest.java | 110 ++++++++++++++++ 3 files changed, 283 insertions(+), 43 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java index 8582c302a73e..ba26b1921a6b 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java @@ -31,7 +31,9 @@ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf { private static final AtomicIntegerFieldUpdater refCntUpdater = AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt"); - private volatile int refCnt = 1; + // even => "real" refcount is (refCnt >>> 1); odd => "real" refcount is 0 + @SuppressWarnings("unused") + private volatile int refCnt = 2; static { long refCntFieldOffset = -1; @@ -47,29 +49,37 @@ public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf { REFCNT_FIELD_OFFSET = refCntFieldOffset; } + private static int realRefCnt(int rawCnt) { + return (rawCnt & 1) != 0 ? 0 : rawCnt >>> 1; + } + protected AbstractReferenceCountedByteBuf(int maxCapacity) { super(maxCapacity); } + private int nonVolatileRawCnt() { + // TODO: Once we compile against later versions of Java we can replace the Unsafe usage here by varhandles. + return REFCNT_FIELD_OFFSET != -1 ? PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET) + : refCntUpdater.get(this); + } + @Override int internalRefCnt() { // Try to do non-volatile read for performance as the ensureAccessible() is racy anyway and only provide // a best-effort guard. - // - // TODO: Once we compile against later versions of Java we can replace the Unsafe usage here by varhandles. - return REFCNT_FIELD_OFFSET != -1 ? PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET) : refCnt(); + return realRefCnt(nonVolatileRawCnt()); } @Override public int refCnt() { - return refCnt; + return realRefCnt(refCntUpdater.get(this)); } /** * An unsafe operation intended for use by a subclass that sets the reference count of the buffer directly */ - protected final void setRefCnt(int refCnt) { - refCntUpdater.set(this, refCnt); + protected final void setRefCnt(int newRefCnt) { + refCntUpdater.set(this, newRefCnt << 1); // overflow OK here } @Override @@ -83,11 +93,18 @@ public ByteBuf retain(int increment) { } private ByteBuf retain0(final int increment) { - int oldRef = refCntUpdater.getAndAdd(this, increment); - if (oldRef <= 0 || oldRef + increment < oldRef) { - // Ensure we don't resurrect (which means the refCnt was 0) and also that we encountered an overflow. - refCntUpdater.getAndAdd(this, -increment); - throw new IllegalReferenceCountException(oldRef, increment); + // all changes to the raw count are 2x the "real" change + int adjustedIncrement = increment << 1; // overflow OK here + int oldRef = refCntUpdater.getAndAdd(this, adjustedIncrement); + if ((oldRef & 1) != 0) { + throw new IllegalReferenceCountException(0, increment); + } + // don't pass 0! + if ((oldRef <= 0 && oldRef + adjustedIncrement >= 0) + || (oldRef >= 0 && oldRef + adjustedIncrement < oldRef)) { + // overflow case + refCntUpdater.getAndAdd(this, -adjustedIncrement); + throw new IllegalReferenceCountException(realRefCnt(oldRef), increment); } return this; } @@ -113,18 +130,57 @@ public boolean release(int decrement) { } private boolean release0(int decrement) { - int oldRef = refCntUpdater.getAndAdd(this, -decrement); - if (oldRef == decrement) { - deallocate(); - return true; + int rawCnt = nonVolatileRawCnt(), realCnt = toLiveRealCnt(rawCnt, decrement); + if (decrement == realCnt) { + if (refCntUpdater.compareAndSet(this, rawCnt, 1)) { + deallocate(); + return true; + } + return retryRelease0(decrement); + } + return releaseNonFinal0(decrement, rawCnt, realCnt); + } + + private boolean releaseNonFinal0(int decrement, int rawCnt, int realCnt) { + if (decrement < realCnt + // all changes to the raw count are 2x the "real" change + && refCntUpdater.compareAndSet(this, rawCnt, rawCnt - (decrement << 1))) { + return false; + } + return retryRelease0(decrement); + } + + private boolean retryRelease0(int decrement) { + for (;;) { + int rawCnt = refCntUpdater.get(this), realCnt = toLiveRealCnt(rawCnt, decrement); + if (decrement == realCnt) { + if (refCntUpdater.compareAndSet(this, rawCnt, 1)) { + deallocate(); + return true; + } + } else if (decrement < realCnt) { + // all changes to the raw count are 2x the "real" change + if (refCntUpdater.compareAndSet(this, rawCnt, rawCnt - (decrement << 1))) { + return false; + } + } else { + throw new IllegalReferenceCountException(realCnt, -decrement); + } + Thread.yield(); // this benefits throughput under high contention } - if (oldRef < decrement || oldRef - decrement > oldRef) { - // Ensure we don't over-release, and avoid underflow. - refCntUpdater.getAndAdd(this, decrement); - throw new IllegalReferenceCountException(oldRef, -decrement); + } + + /** + * Like {@link #realRefCnt(int)} but throws if refCnt == 0 + */ + private static int toLiveRealCnt(int rawCnt, int decrement) { + if ((rawCnt & 1) == 0) { + return rawCnt >>> 1; } - return false; + // odd rawCnt => already deallocated + throw new IllegalReferenceCountException(0, -decrement); } + /** * Called once {@link #refCnt()} is equals 0. */ diff --git a/common/src/main/java/io/netty/util/AbstractReferenceCounted.java b/common/src/main/java/io/netty/util/AbstractReferenceCounted.java index fe20f92a2e8d..b7480c4eb872 100644 --- a/common/src/main/java/io/netty/util/AbstractReferenceCounted.java +++ b/common/src/main/java/io/netty/util/AbstractReferenceCounted.java @@ -15,30 +15,58 @@ */ package io.netty.util; +import static io.netty.util.internal.ObjectUtil.checkPositive; + import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import static io.netty.util.internal.ObjectUtil.checkPositive; +import io.netty.util.internal.PlatformDependent; /** * Abstract base class for classes wants to implement {@link ReferenceCounted}. */ public abstract class AbstractReferenceCounted implements ReferenceCounted { - + private static final long REFCNT_FIELD_OFFSET; private static final AtomicIntegerFieldUpdater refCntUpdater = AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCounted.class, "refCnt"); - private volatile int refCnt = 1; + // even => "real" refcount is (refCnt >>> 1); odd => "real" refcount is 0 + @SuppressWarnings("unused") + private volatile int refCnt = 2; + + static { + long refCntFieldOffset = -1; + try { + if (PlatformDependent.hasUnsafe()) { + refCntFieldOffset = PlatformDependent.objectFieldOffset( + AbstractReferenceCounted.class.getDeclaredField("refCnt")); + } + } catch (Throwable ignore) { + refCntFieldOffset = -1; + } + + REFCNT_FIELD_OFFSET = refCntFieldOffset; + } + + private static int realRefCnt(int rawCnt) { + return (rawCnt & 1) != 0 ? 0 : rawCnt >>> 1; + } + + private int nonVolatileRawCnt() { + // TODO: Once we compile against later versions of Java we can replace the Unsafe usage here by varhandles. + return REFCNT_FIELD_OFFSET != -1 ? PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET) + : refCntUpdater.get(this); + } @Override - public final int refCnt() { - return refCnt; + public int refCnt() { + return realRefCnt(refCntUpdater.get(this)); } /** * An unsafe operation intended for use by a subclass that sets the reference count of the buffer directly */ - protected final void setRefCnt(int refCnt) { - refCntUpdater.set(this, refCnt); + protected final void setRefCnt(int newRefCnt) { + refCntUpdater.set(this, newRefCnt << 1); // overflow OK here } @Override @@ -51,12 +79,19 @@ public ReferenceCounted retain(int increment) { return retain0(checkPositive(increment, "increment")); } - private ReferenceCounted retain0(int increment) { - int oldRef = refCntUpdater.getAndAdd(this, increment); - if (oldRef <= 0 || oldRef + increment < oldRef) { - // Ensure we don't resurrect (which means the refCnt was 0) and also that we encountered an overflow. - refCntUpdater.getAndAdd(this, -increment); - throw new IllegalReferenceCountException(oldRef, increment); + private ReferenceCounted retain0(final int increment) { + // all changes to the raw count are 2x the "real" change + int adjustedIncrement = increment << 1; // overflow OK here + int oldRef = refCntUpdater.getAndAdd(this, adjustedIncrement); + if ((oldRef & 1) != 0) { + throw new IllegalReferenceCountException(0, increment); + } + // don't pass 0! + if ((oldRef <= 0 && oldRef + adjustedIncrement >= 0) + || (oldRef >= 0 && oldRef + adjustedIncrement < oldRef)) { + // overflow case + refCntUpdater.getAndAdd(this, -adjustedIncrement); + throw new IllegalReferenceCountException(realRefCnt(oldRef), increment); } return this; } @@ -77,16 +112,55 @@ public boolean release(int decrement) { } private boolean release0(int decrement) { - int oldRef = refCntUpdater.getAndAdd(this, -decrement); - if (oldRef == decrement) { - deallocate(); - return true; - } else if (oldRef < decrement || oldRef - decrement > oldRef) { - // Ensure we don't over-release, and avoid underflow. - refCntUpdater.getAndAdd(this, decrement); - throw new IllegalReferenceCountException(oldRef, -decrement); + int rawCnt = nonVolatileRawCnt(), realCnt = toLiveRealCnt(rawCnt, decrement); + if (decrement == realCnt) { + if (refCntUpdater.compareAndSet(this, rawCnt, 1)) { + deallocate(); + return true; + } + return retryRelease0(decrement); + } + return releaseNonFinal0(decrement, rawCnt, realCnt); + } + + private boolean releaseNonFinal0(int decrement, int rawCnt, int realCnt) { + if (decrement < realCnt + // all changes to the raw count are 2x the "real" change + && refCntUpdater.compareAndSet(this, rawCnt, rawCnt - (decrement << 1))) { + return false; + } + return retryRelease0(decrement); + } + + private boolean retryRelease0(int decrement) { + for (;;) { + int rawCnt = refCntUpdater.get(this), realCnt = toLiveRealCnt(rawCnt, decrement); + if (decrement == realCnt) { + if (refCntUpdater.compareAndSet(this, rawCnt, 1)) { + deallocate(); + return true; + } + } else if (decrement < realCnt) { + // all changes to the raw count are 2x the "real" change + if (refCntUpdater.compareAndSet(this, rawCnt, rawCnt - (decrement << 1))) { + return false; + } + } else { + throw new IllegalReferenceCountException(realCnt, -decrement); + } + Thread.yield(); // this benefits throughput under high contention + } + } + + /** + * Like {@link #realRefCnt(int)} but throws if refCnt == 0 + */ + private static int toLiveRealCnt(int rawCnt, int decrement) { + if ((rawCnt & 1) == 0) { + return rawCnt >>> 1; } - return false; + // odd rawCnt => already deallocated + throw new IllegalReferenceCountException(0, -decrement); } /** diff --git a/common/src/test/java/io/netty/util/AbstractReferenceCountedTest.java b/common/src/test/java/io/netty/util/AbstractReferenceCountedTest.java index 5af113490f8e..26762355fe29 100644 --- a/common/src/test/java/io/netty/util/AbstractReferenceCountedTest.java +++ b/common/src/test/java/io/netty/util/AbstractReferenceCountedTest.java @@ -15,8 +15,17 @@ */ package io.netty.util; +import io.netty.util.internal.ThreadLocalRandom; import org.junit.Test; +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -74,6 +83,107 @@ public void testRetainResurrect2() { referenceCounted.retain(2); } + @Test(timeout = 30000) + public void testRetainFromMultipleThreadsThrowsReferenceCountException() throws Exception { + int threads = 4; + Queue> futures = new ArrayDeque>(threads); + ExecutorService service = Executors.newFixedThreadPool(threads); + final AtomicInteger refCountExceptions = new AtomicInteger(); + + try { + for (int i = 0; i < 10000; i++) { + final AbstractReferenceCounted referenceCounted = newReferenceCounted(); + final CountDownLatch retainLatch = new CountDownLatch(1); + assertTrue(referenceCounted.release()); + + for (int a = 0; a < threads; a++) { + final int retainCnt = ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE); + futures.add(service.submit(new Runnable() { + @Override + public void run() { + try { + retainLatch.await(); + try { + referenceCounted.retain(retainCnt); + } catch (IllegalReferenceCountException e) { + refCountExceptions.incrementAndGet(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + })); + } + retainLatch.countDown(); + + for (;;) { + Future f = futures.poll(); + if (f == null) { + break; + } + f.get(); + } + assertEquals(4, refCountExceptions.get()); + refCountExceptions.set(0); + } + } finally { + service.shutdown(); + } + } + + @Test(timeout = 30000) + public void testReleaseFromMultipleThreadsThrowsReferenceCountException() throws Exception { + int threads = 4; + Queue> futures = new ArrayDeque>(threads); + ExecutorService service = Executors.newFixedThreadPool(threads); + final AtomicInteger refCountExceptions = new AtomicInteger(); + + try { + for (int i = 0; i < 10000; i++) { + final AbstractReferenceCounted referenceCounted = newReferenceCounted(); + final CountDownLatch releaseLatch = new CountDownLatch(1); + final AtomicInteger releasedCount = new AtomicInteger(); + + for (int a = 0; a < threads; a++) { + final AtomicInteger releaseCnt = new AtomicInteger(0); + + futures.add(service.submit(new Runnable() { + @Override + public void run() { + try { + releaseLatch.await(); + try { + if (referenceCounted.release(releaseCnt.incrementAndGet())) { + releasedCount.incrementAndGet(); + } + } catch (IllegalReferenceCountException e) { + refCountExceptions.incrementAndGet(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + })); + } + releaseLatch.countDown(); + + for (;;) { + Future f = futures.poll(); + if (f == null) { + break; + } + f.get(); + } + assertEquals(3, refCountExceptions.get()); + assertEquals(1, releasedCount.get()); + + refCountExceptions.set(0); + } + } finally { + service.shutdown(); + } + } + private static AbstractReferenceCounted newReferenceCounted() { return new AbstractReferenceCounted() { @Override From afcb4a37d3a704c435f8312f9547309d6f1003ac Mon Sep 17 00:00:00 2001 From: root Date: Thu, 29 Nov 2018 11:14:20 +0000 Subject: [PATCH 291/417] [maven-release-plugin] prepare release netty-4.1.32.Final --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 76 insertions(+), 72 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index b250a86c3aa7..2ee4d3ef122b 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-all diff --git a/bom/pom.xml b/bom/pom.xml index e1ee7ff5ef83..a15587c2a31d 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.32.Final-SNAPSHOT + 4.1.32.Final pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.32.Final @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec-dns - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec-haproxy - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec-http - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec-http2 - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec-memcache - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec-mqtt - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec-redis - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec-smtp - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec-socks - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec-stomp - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-codec-xml - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-common - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-dev-tools - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-handler - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-handler-proxy - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-resolver - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-resolver-dns - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-transport - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-transport-rxtx - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-transport-sctp - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-transport-udt - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-example - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-all - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-transport-native-unix-common - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-transport-native-unix-common - 4.1.32.Final-SNAPSHOT + 4.1.32.Final linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.32.Final-SNAPSHOT + 4.1.32.Final osx-x86_64 io.netty netty-transport-native-epoll - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-transport-native-epoll - 4.1.32.Final-SNAPSHOT + 4.1.32.Final linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.32.Final-SNAPSHOT + 4.1.32.Final io.netty netty-transport-native-kqueue - 4.1.32.Final-SNAPSHOT + 4.1.32.Final osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 73fedbf899b2..f0f55767f8d0 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 39567c621789..e2b758b574fe 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index f2d7bb210045..6d823e7a1441 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 6f1efbc31ec0..cc55c4e634e3 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 0e713817f9cc..f9f5426c610b 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index ea1eb8273b07..5137b5f8772e 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index a18d1396d27c..5c0fe2ba0365 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 08aa0bf62760..b713b2684631 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index b67e516ce3a8..ada16e1a0232 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 28dbafbd1bdb..cd86a4167279 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index ec948a6fa985..05e27bb94e52 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index c8341c4306d8..d3b6153561db 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 646c7f617cf7..5b8dd91208da 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-codec diff --git a/common/pom.xml b/common/pom.xml index dce8b4602d39..ddc91d598faf 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 65858c73df90..dc51220ee4ad 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.32.Final-SNAPSHOT + 4.1.32.Final Netty/Dev-Tools @@ -50,4 +50,8 @@ + + + netty-4.1.32.Final + diff --git a/example/pom.xml b/example/pom.xml index 7d1adb27fa37..1d6c9c5b9cdb 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 5fef26836f4a..44db2e36006a 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index 55dca310ff68..774bdc5e82c6 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 5152ccd019bd..6d404c5c9820 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-microbench diff --git a/pom.xml b/pom.xml index 6dfb062c474b..f935b12c0881 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.32.Final-SNAPSHOT + 4.1.32.Final Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.32.Final diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index f6be79007b4d..671605cf36da 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index 1be3590be6e7..d330aba652c4 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index dbf6401f7564..7a27511dbc79 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 0d16cac0814c..af8471380ca2 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 86ac300dbbb7..c153069b24e2 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 175b4a758897..1225a95922e0 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index 17a8a94b2a1c..8c19a39ec191 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index a5a937d91ebf..28f642caa7d8 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 1af08c5b4008..81f8595d5b04 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index c2bfa5f4c77d..4e94159cdf7e 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 03547827160f..1efdab1c1d55 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 75de5014eee9..40c3f93a0c7e 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index f09a86f274c0..a8fb91b2f097 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index d62d24233fe9..a86bcbf96015 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 5a89dbae93bc..d95e253ab441 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index e504fa6c3969..1bbb32b6c327 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final-SNAPSHOT + 4.1.32.Final netty-transport From 8eb313072e3f976df389de7f5394cfe02539f03f Mon Sep 17 00:00:00 2001 From: root Date: Thu, 29 Nov 2018 11:15:09 +0000 Subject: [PATCH 292/417] [maven-release-plugin] prepare for next development iteration --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 72 insertions(+), 76 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 2ee4d3ef122b..5f45e81705de 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-all diff --git a/bom/pom.xml b/bom/pom.xml index a15587c2a31d..b57dc86b0d5c 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.32.Final + 4.1.33.Final-SNAPSHOT pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.32.Final + HEAD @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec-dns - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec-haproxy - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec-http - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec-http2 - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec-memcache - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec-mqtt - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec-redis - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec-smtp - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec-socks - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec-stomp - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-codec-xml - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-common - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-dev-tools - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-handler - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-handler-proxy - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-resolver - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-resolver-dns - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-transport - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-transport-rxtx - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-transport-sctp - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-transport-udt - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-example - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-all - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.32.Final + 4.1.33.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.32.Final + 4.1.33.Final-SNAPSHOT osx-x86_64 io.netty netty-transport-native-epoll - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-transport-native-epoll - 4.1.32.Final + 4.1.33.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.32.Final + 4.1.33.Final-SNAPSHOT io.netty netty-transport-native-kqueue - 4.1.32.Final + 4.1.33.Final-SNAPSHOT osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index f0f55767f8d0..1083fef393b8 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index e2b758b574fe..11a34dce94e0 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 6d823e7a1441..a73897327b0b 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index cc55c4e634e3..768e1783aaac 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index f9f5426c610b..2f2609d5a220 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 5137b5f8772e..9a20defb5d18 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 5c0fe2ba0365..85c72fb66b06 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index b713b2684631..d8f80e555a34 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index ada16e1a0232..ce59f5fcdd17 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index cd86a4167279..2d6ce44674b3 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 05e27bb94e52..63a298df92d9 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index d3b6153561db..0c76e7033204 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 5b8dd91208da..c26b6e0a9b1a 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-codec diff --git a/common/pom.xml b/common/pom.xml index ddc91d598faf..2722f23dfd04 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index dc51220ee4ad..176a6e16dbcb 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.32.Final + 4.1.33.Final-SNAPSHOT Netty/Dev-Tools @@ -50,8 +50,4 @@ - - - netty-4.1.32.Final - diff --git a/example/pom.xml b/example/pom.xml index 1d6c9c5b9cdb..4174c9cefd45 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 44db2e36006a..279b4100ed65 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index 774bdc5e82c6..6318eb1fe67a 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 6d404c5c9820..6ae59054833c 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-microbench diff --git a/pom.xml b/pom.xml index f935b12c0881..a53264ef4508 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.32.Final + 4.1.33.Final-SNAPSHOT Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.32.Final + HEAD diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index 671605cf36da..3d48f9055516 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index d330aba652c4..5546169a631b 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 7a27511dbc79..a36d3a444bbc 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index af8471380ca2..2a2f9084aeb4 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index c153069b24e2..096235477747 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 1225a95922e0..04b4f64830a4 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index 8c19a39ec191..a0d615b37d70 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 28f642caa7d8..65bd3a483fb7 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 81f8595d5b04..f804b7b02fce 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 4e94159cdf7e..50188e50efc2 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 1efdab1c1d55..f366cc1ca6b2 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 40c3f93a0c7e..b7f764123d2c 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index a8fb91b2f097..3d54aabb6387 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index a86bcbf96015..ef29309f3ade 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index d95e253ab441..38cf1ae2d5e1 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 1bbb32b6c327..10917e3756d1 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.32.Final + 4.1.33.Final-SNAPSHOT netty-transport From a0c3081d8264b3e16d98a87a90250f26c6d9ed53 Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Thu, 29 Nov 2018 10:45:52 -0800 Subject: [PATCH 293/417] Reduce http2 buffer slicing (#8598) Motivation DefaultHttp2FrameReader currently does a fair amount of "intermediate" slicing which can be avoided. Modifications Avoid slicing the input buffer in DefaultHttp2FrameReader until necessary. In one instance this also means retainedSlice can be used instead (which may also avoid allocating). Results Less allocations when using http2. --- .../codec/http2/DefaultHttp2FrameReader.java | 100 +++++++++--------- .../codec/http2/DefaultHttp2FrameWriter.java | 2 +- 2 files changed, 50 insertions(+), 52 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameReader.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameReader.java index 63e184bc7b4b..cc63b2847461 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameReader.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameReader.java @@ -239,8 +239,8 @@ private void processPayloadState(ChannelHandlerContext ctx, ByteBuf in, Http2Fra return; } - // Get a view of the buffer for the size of the payload. - ByteBuf payload = in.readSlice(payloadLength); + // Only process up to payloadLength bytes. + int payloadEndIndex = in.readerIndex() + payloadLength; // We have consumed the data, next time we read we will be expecting to read a frame header. readingHeaders = true; @@ -248,39 +248,40 @@ private void processPayloadState(ChannelHandlerContext ctx, ByteBuf in, Http2Fra // Read the payload and fire the frame event to the listener. switch (frameType) { case DATA: - readDataFrame(ctx, payload, listener); + readDataFrame(ctx, in, payloadEndIndex, listener); break; case HEADERS: - readHeadersFrame(ctx, payload, listener); + readHeadersFrame(ctx, in, payloadEndIndex, listener); break; case PRIORITY: - readPriorityFrame(ctx, payload, listener); + readPriorityFrame(ctx, in, listener); break; case RST_STREAM: - readRstStreamFrame(ctx, payload, listener); + readRstStreamFrame(ctx, in, listener); break; case SETTINGS: - readSettingsFrame(ctx, payload, listener); + readSettingsFrame(ctx, in, listener); break; case PUSH_PROMISE: - readPushPromiseFrame(ctx, payload, listener); + readPushPromiseFrame(ctx, in, payloadEndIndex, listener); break; case PING: - readPingFrame(ctx, payload.readLong(), listener); + readPingFrame(ctx, in.readLong(), listener); break; case GO_AWAY: - readGoAwayFrame(ctx, payload, listener); + readGoAwayFrame(ctx, in, payloadEndIndex, listener); break; case WINDOW_UPDATE: - readWindowUpdateFrame(ctx, payload, listener); + readWindowUpdateFrame(ctx, in, listener); break; case CONTINUATION: - readContinuationFrame(payload, listener); + readContinuationFrame(in, payloadEndIndex, listener); break; default: - readUnknownFrame(ctx, payload, listener); + readUnknownFrame(ctx, in, payloadEndIndex, listener); break; } + in.readerIndex(payloadEndIndex); } private void verifyDataFrame() throws Http2Exception { @@ -408,21 +409,20 @@ private void verifyUnknownFrame() throws Http2Exception { verifyNotProcessingHeaders(); } - private void readDataFrame(ChannelHandlerContext ctx, ByteBuf payload, + private void readDataFrame(ChannelHandlerContext ctx, ByteBuf payload, int payloadEndIndex, Http2FrameListener listener) throws Http2Exception { int padding = readPadding(payload); verifyPadding(padding); // Determine how much data there is to read by removing the trailing // padding. - int dataLength = lengthWithoutTrailingPadding(payload.readableBytes(), padding); + int dataLength = lengthWithoutTrailingPadding(payloadEndIndex - payload.readerIndex(), padding); ByteBuf data = payload.readSlice(dataLength); listener.onDataRead(ctx, streamId, data, padding, flags.endOfStream()); - payload.skipBytes(payload.readableBytes()); } - private void readHeadersFrame(final ChannelHandlerContext ctx, ByteBuf payload, + private void readHeadersFrame(final ChannelHandlerContext ctx, ByteBuf payload, int payloadEndIndex, Http2FrameListener listener) throws Http2Exception { final int headersStreamId = streamId; final Http2Flags headersFlags = flags; @@ -439,7 +439,7 @@ private void readHeadersFrame(final ChannelHandlerContext ctx, ByteBuf payload, throw streamError(streamId, PROTOCOL_ERROR, "A stream cannot depend on itself."); } final short weight = (short) (payload.readUnsignedByte() + 1); - final ByteBuf fragment = payload.readSlice(lengthWithoutTrailingPadding(payload.readableBytes(), padding)); + final int lenToRead = lengthWithoutTrailingPadding(payloadEndIndex - payload.readerIndex(), padding); // Create a handler that invokes the listener when the header block is complete. headersContinuation = new HeadersContinuation() { @@ -449,10 +449,10 @@ public int getStreamId() { } @Override - public void processFragment(boolean endOfHeaders, ByteBuf fragment, + public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len, Http2FrameListener listener) throws Http2Exception { final HeadersBlockBuilder hdrBlockBuilder = headersBlockBuilder(); - hdrBlockBuilder.addFragment(fragment, ctx.alloc(), endOfHeaders); + hdrBlockBuilder.addFragment(fragment, len, ctx.alloc(), endOfHeaders); if (endOfHeaders) { listener.onHeadersRead(ctx, headersStreamId, hdrBlockBuilder.headers(), streamDependency, weight, exclusive, padding, headersFlags.endOfStream()); @@ -461,7 +461,7 @@ public void processFragment(boolean endOfHeaders, ByteBuf fragment, }; // Process the initial fragment, invoking the listener's callback if end of headers. - headersContinuation.processFragment(flags.endOfHeaders(), fragment, listener); + headersContinuation.processFragment(flags.endOfHeaders(), payload, lenToRead, listener); resetHeadersContinuationIfEnd(flags.endOfHeaders()); return; } @@ -475,10 +475,10 @@ public int getStreamId() { } @Override - public void processFragment(boolean endOfHeaders, ByteBuf fragment, + public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len, Http2FrameListener listener) throws Http2Exception { final HeadersBlockBuilder hdrBlockBuilder = headersBlockBuilder(); - hdrBlockBuilder.addFragment(fragment, ctx.alloc(), endOfHeaders); + hdrBlockBuilder.addFragment(fragment, len, ctx.alloc(), endOfHeaders); if (endOfHeaders) { listener.onHeadersRead(ctx, headersStreamId, hdrBlockBuilder.headers(), padding, headersFlags.endOfStream()); @@ -487,8 +487,8 @@ public void processFragment(boolean endOfHeaders, ByteBuf fragment, }; // Process the initial fragment, invoking the listener's callback if end of headers. - final ByteBuf fragment = payload.readSlice(lengthWithoutTrailingPadding(payload.readableBytes(), padding)); - headersContinuation.processFragment(flags.endOfHeaders(), fragment, listener); + int len = lengthWithoutTrailingPadding(payloadEndIndex - payload.readerIndex(), padding); + headersContinuation.processFragment(flags.endOfHeaders(), payload, len, listener); resetHeadersContinuationIfEnd(flags.endOfHeaders()); } @@ -543,7 +543,7 @@ private void readSettingsFrame(ChannelHandlerContext ctx, ByteBuf payload, } } - private void readPushPromiseFrame(final ChannelHandlerContext ctx, ByteBuf payload, + private void readPushPromiseFrame(final ChannelHandlerContext ctx, ByteBuf payload, int payloadEndIndex, Http2FrameListener listener) throws Http2Exception { final int pushPromiseStreamId = streamId; final int padding = readPadding(payload); @@ -558,9 +558,9 @@ public int getStreamId() { } @Override - public void processFragment(boolean endOfHeaders, ByteBuf fragment, + public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len, Http2FrameListener listener) throws Http2Exception { - headersBlockBuilder().addFragment(fragment, ctx.alloc(), endOfHeaders); + headersBlockBuilder().addFragment(fragment, len, ctx.alloc(), endOfHeaders); if (endOfHeaders) { listener.onPushPromiseRead(ctx, pushPromiseStreamId, promisedStreamId, headersBlockBuilder().headers(), padding); @@ -569,8 +569,8 @@ public void processFragment(boolean endOfHeaders, ByteBuf fragment, }; // Process the initial fragment, invoking the listener's callback if end of headers. - final ByteBuf fragment = payload.readSlice(lengthWithoutTrailingPadding(payload.readableBytes(), padding)); - headersContinuation.processFragment(flags.endOfHeaders(), fragment, listener); + int len = lengthWithoutTrailingPadding(payloadEndIndex - payload.readerIndex(), padding); + headersContinuation.processFragment(flags.endOfHeaders(), payload, len, listener); resetHeadersContinuationIfEnd(flags.endOfHeaders()); } @@ -583,11 +583,11 @@ private void readPingFrame(ChannelHandlerContext ctx, long data, } } - private static void readGoAwayFrame(ChannelHandlerContext ctx, ByteBuf payload, + private static void readGoAwayFrame(ChannelHandlerContext ctx, ByteBuf payload, int payloadEndIndex, Http2FrameListener listener) throws Http2Exception { int lastStreamId = readUnsignedInt(payload); long errorCode = payload.readUnsignedInt(); - ByteBuf debugData = payload.readSlice(payload.readableBytes()); + ByteBuf debugData = payload.readSlice(payloadEndIndex - payload.readerIndex()); listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData); } @@ -601,18 +601,17 @@ private void readWindowUpdateFrame(ChannelHandlerContext ctx, ByteBuf payload, listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement); } - private void readContinuationFrame(ByteBuf payload, Http2FrameListener listener) + private void readContinuationFrame(ByteBuf payload, int payloadEndIndex, Http2FrameListener listener) throws Http2Exception { // Process the initial fragment, invoking the listener's callback if end of headers. - final ByteBuf continuationFragment = payload.readSlice(payload.readableBytes()); - headersContinuation.processFragment(flags.endOfHeaders(), continuationFragment, - listener); + headersContinuation.processFragment(flags.endOfHeaders(), payload, + payloadEndIndex - payload.readerIndex(), listener); resetHeadersContinuationIfEnd(flags.endOfHeaders()); } - private void readUnknownFrame(ChannelHandlerContext ctx, ByteBuf payload, Http2FrameListener listener) - throws Http2Exception { - payload = payload.readSlice(payload.readableBytes()); + private void readUnknownFrame(ChannelHandlerContext ctx, ByteBuf payload, + int payloadEndIndex, Http2FrameListener listener) throws Http2Exception { + payload = payload.readSlice(payloadEndIndex - payload.readerIndex()); listener.onUnknownFrame(ctx, frameType, streamId, flags, payload); } @@ -664,7 +663,7 @@ private abstract class HeadersContinuation { * @param fragment the fragment of the header block to be added. * @param listener the listener to be notified if the header block is completed. */ - abstract void processFragment(boolean endOfHeaders, ByteBuf fragment, + abstract void processFragment(boolean endOfHeaders, ByteBuf fragment, int len, Http2FrameListener listener) throws Http2Exception; final HeadersBlockBuilder headersBlockBuilder() { @@ -704,33 +703,32 @@ private void headerSizeExceeded() throws Http2Exception { * This is used for an optimization for when the first fragment is the full * block. In that case, the buffer is used directly without copying. */ - final void addFragment(ByteBuf fragment, ByteBufAllocator alloc, boolean endOfHeaders) throws Http2Exception { + final void addFragment(ByteBuf fragment, int len, ByteBufAllocator alloc, + boolean endOfHeaders) throws Http2Exception { if (headerBlock == null) { - if (fragment.readableBytes() > headersDecoder.configuration().maxHeaderListSizeGoAway()) { + if (len > headersDecoder.configuration().maxHeaderListSizeGoAway()) { headerSizeExceeded(); } if (endOfHeaders) { // Optimization - don't bother copying, just use the buffer as-is. Need // to retain since we release when the header block is built. - headerBlock = fragment.retain(); + headerBlock = fragment.readRetainedSlice(len); } else { - headerBlock = alloc.buffer(fragment.readableBytes()); - headerBlock.writeBytes(fragment); + headerBlock = alloc.buffer(len).writeBytes(fragment, len); } return; } - if (headersDecoder.configuration().maxHeaderListSizeGoAway() - fragment.readableBytes() < + if (headersDecoder.configuration().maxHeaderListSizeGoAway() - len < headerBlock.readableBytes()) { headerSizeExceeded(); } - if (headerBlock.isWritable(fragment.readableBytes())) { + if (headerBlock.isWritable(len)) { // The buffer can hold the requested bytes, just write it directly. - headerBlock.writeBytes(fragment); + headerBlock.writeBytes(fragment, len); } else { // Allocate a new buffer that is big enough to hold the entire header block so far. - ByteBuf buf = alloc.buffer(headerBlock.readableBytes() + fragment.readableBytes()); - buf.writeBytes(headerBlock); - buf.writeBytes(fragment); + ByteBuf buf = alloc.buffer(headerBlock.readableBytes() + len); + buf.writeBytes(headerBlock).writeBytes(fragment, len); headerBlock.release(); headerBlock = buf; } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java index 3fa84137042d..1e4cdcd93704 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java @@ -195,7 +195,7 @@ public ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, ByteBuf ctx.write(lastFrame, promiseAggregator.newPromise()); // Write the payload. - lastFrame = data.readSlice(maxFrameSize); + lastFrame = data.readableBytes() != maxFrameSize ? data.readSlice(maxFrameSize) : data; data = null; ctx.write(lastFrame, promiseAggregator.newPromise()); } From d05666ae2d2068da7ee031a8bfc1ca572dbcc3f8 Mon Sep 17 00:00:00 2001 From: Julien Hoarau Date: Sat, 1 Dec 2018 20:47:18 +1100 Subject: [PATCH 294/417] Set-Cookie headers should not be combined (#8611) Motivation: According to the HTTP spec set-cookie headers should not be combined because they are not using the list syntax. Modifications: Do not combine set-cookie headers. Result: Set-Cookie headers won't be combined anymore --- .../codec/http/CombinedHttpHeaders.java | 11 ++-- .../codec/http/CombinedHttpHeadersTest.java | 50 +++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java index 2d43b7ad04a3..ae494934d7c5 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; +import static io.netty.handler.codec.http.HttpHeaderNames.SET_COOKIE; import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER; import static io.netty.util.internal.StringUtil.COMMA; import static io.netty.util.internal.StringUtil.unescapeCsvFields; @@ -87,7 +88,7 @@ public CombinedHttpHeadersImpl(HashingStrategy nameHashingStrategy @Override public Iterator valueIterator(CharSequence name) { Iterator itr = super.valueIterator(name); - if (!itr.hasNext()) { + if (!itr.hasNext() || cannotBeCombined(name)) { return itr; } Iterator unescapedItr = unescapeCsvFields(itr.next()).iterator(); @@ -100,7 +101,7 @@ public Iterator valueIterator(CharSequence name) { @Override public List getAll(CharSequence name) { List values = super.getAll(name); - if (values.isEmpty()) { + if (values.isEmpty() || cannotBeCombined(name)) { return values; } if (values.size() != 1) { @@ -213,9 +214,13 @@ public CombinedHttpHeadersImpl setObject(CharSequence name, Iterable values) return this; } + private static boolean cannotBeCombined(CharSequence name) { + return SET_COOKIE.contentEqualsIgnoreCase(name); + } + private CombinedHttpHeadersImpl addEscapedValue(CharSequence name, CharSequence escapedValue) { CharSequence currentValue = super.get(name); - if (currentValue == null) { + if (currentValue == null || cannotBeCombined(name)) { super.add(name, escapedValue); } else { super.set(name, commaSeparateEscapedValues(currentValue, escapedValue)); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/CombinedHttpHeadersTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/CombinedHttpHeadersTest.java index e0433efb97c6..c63c885a9554 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/CombinedHttpHeadersTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/CombinedHttpHeadersTest.java @@ -22,9 +22,12 @@ import java.util.Collections; import java.util.Iterator; +import static io.netty.handler.codec.http.HttpHeaderNames.SET_COOKIE; import static io.netty.util.AsciiString.contentEquals; +import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; public class CombinedHttpHeadersTest { @@ -66,6 +69,28 @@ public void addCombinedHeadersWhenNotEmpty() { assertEquals("a,b,c", headers.get(HEADER_NAME).toString()); } + @Test + public void dontCombineSetCookieHeaders() { + final CombinedHttpHeaders headers = newCombinedHttpHeaders(); + headers.add(SET_COOKIE, "a"); + final CombinedHttpHeaders otherHeaders = newCombinedHttpHeaders(); + otherHeaders.add(SET_COOKIE, "b"); + otherHeaders.add(SET_COOKIE, "c"); + headers.add(otherHeaders); + assertThat(headers.getAll(SET_COOKIE), hasSize(3)); + } + + @Test + public void dontCombineSetCookieHeadersRegardlessOfCase() { + final CombinedHttpHeaders headers = newCombinedHttpHeaders(); + headers.add("Set-Cookie", "a"); + final CombinedHttpHeaders otherHeaders = newCombinedHttpHeaders(); + otherHeaders.add("set-cookie", "b"); + otherHeaders.add("SET-COOKIE", "c"); + headers.add(otherHeaders); + assertThat(headers.getAll(SET_COOKIE), hasSize(3)); + } + @Test public void setCombinedHeadersWhenNotEmpty() { final CombinedHttpHeaders headers = newCombinedHttpHeaders(); @@ -274,6 +299,15 @@ public void testGetAll() { assertEquals(Arrays.asList("a,b,c"), headers.getAll(HEADER_NAME)); } + @Test + public void getAllDontCombineSetCookie() { + final CombinedHttpHeaders headers = newCombinedHttpHeaders(); + headers.add(SET_COOKIE, "a"); + headers.add(SET_COOKIE, "b"); + assertThat(headers.getAll(SET_COOKIE), hasSize(2)); + assertEquals(Arrays.asList("a", "b"), headers.getAll(SET_COOKIE)); + } + @Test public void owsTrimming() { final CombinedHttpHeaders headers = newCombinedHttpHeaders(); @@ -314,6 +348,22 @@ public void valueIterator() { assertValueIterator(headers.valueCharSequenceIterator(HEADER_NAME)); } + @Test + public void nonCombinableHeaderIterator() { + final CombinedHttpHeaders headers = newCombinedHttpHeaders(); + headers.add(SET_COOKIE, "c"); + headers.add(SET_COOKIE, "b"); + headers.add(SET_COOKIE, "a"); + + final Iterator strItr = headers.valueStringIterator(SET_COOKIE); + assertTrue(strItr.hasNext()); + assertEquals("a", strItr.next()); + assertTrue(strItr.hasNext()); + assertEquals("b", strItr.next()); + assertTrue(strItr.hasNext()); + assertEquals("c", strItr.next()); + } + private static void assertValueIterator(Iterator strItr) { assertTrue(strItr.hasNext()); assertEquals("a", strItr.next()); From dcbd7c492b79e4f72731aac3614672d5efeaf90c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 4 Dec 2018 11:59:10 +0100 Subject: [PATCH 295/417] Update to OpenJDK 12 ea22 (#8618) Motivation: We should use the latest OpenJDK 12 release when running tests against Java12. Modifications: - Update to OpenJDK 12 ea22. - Update pax exam version - skip OSGI testsuite on Java12 as it does not work ea22 yet. Result: Use latest OpenJDK 12 version when running on the CI. --- docker/docker-compose.centos-6.112.yaml | 2 +- docker/docker-compose.centos-7.112.yaml | 2 +- pom.xml | 2 ++ testsuite-osgi/pom.xml | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.centos-6.112.yaml b/docker/docker-compose.centos-6.112.yaml index ded410bea7dc..c49744ca717e 100644 --- a/docker/docker-compose.centos-6.112.yaml +++ b/docker/docker-compose.centos-6.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "openjdk@1.12.0-19" + java_version : "openjdk@1.12.0-22" test: image: netty:centos-6-1.12 diff --git a/docker/docker-compose.centos-7.112.yaml b/docker/docker-compose.centos-7.112.yaml index 9eb4fb1da720..93c05349b96e 100644 --- a/docker/docker-compose.centos-7.112.yaml +++ b/docker/docker-compose.centos-7.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "openjdk@1.12.0-19" + java_version : "openjdk@1.12.0-22" test: image: netty:centos-7-1.12 diff --git a/pom.xml b/pom.xml index a53264ef4508..60bfa29c78b6 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,8 @@ 1.7 1.7 + + true diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 04b4f64830a4..01a39284422c 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -29,7 +29,7 @@ Netty/Testsuite/OSGI - 4.9.1 + 4.13.0 --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED From 268035742317ced8e8d43b9998244f4f0e53316a Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 4 Dec 2018 15:26:05 +0100 Subject: [PATCH 296/417] =?UTF-8?q?Provide=20a=20way=20to=20cache=20the=20?= =?UTF-8?q?internal=20nioBuffer=20of=20the=20PooledByteBuffer=E2=80=A6=20(?= =?UTF-8?q?#8603)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: Often a temporary ByteBuffer is used which can be cached to reduce the GC pressure. Modifications: Cache the ByteBuffer in the PoolThreadCache as well. Result: Less GC. --- .../main/java/io/netty/buffer/PoolArena.java | 20 +++---- .../main/java/io/netty/buffer/PoolChunk.java | 58 ++++++++++++++----- .../java/io/netty/buffer/PoolChunkList.java | 20 +++---- .../java/io/netty/buffer/PoolThreadCache.java | 29 ++++++---- .../java/io/netty/buffer/PooledByteBuf.java | 16 ++--- .../netty/buffer/PooledByteBufAllocator.java | 8 +++ .../buffer/PooledUnsafeDirectByteBuf.java | 6 +- 7 files changed, 97 insertions(+), 60 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/PoolArena.java b/buffer/src/main/java/io/netty/buffer/PoolArena.java index 08d7a38e91e2..3bc948be412d 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolArena.java +++ b/buffer/src/main/java/io/netty/buffer/PoolArena.java @@ -205,7 +205,7 @@ private void allocate(PoolThreadCache cache, PooledByteBuf buf, final int req assert s.doNotDestroy && s.elemSize == normCapacity; long handle = s.allocate(); assert handle >= 0; - s.chunk.initBufWithSubpage(buf, handle, reqCapacity); + s.chunk.initBufWithSubpage(buf, null, handle, reqCapacity); incTinySmallAllocation(tiny); return; } @@ -242,9 +242,8 @@ private void allocateNormal(PooledByteBuf buf, int reqCapacity, int normCapac // Add a new chunk. PoolChunk c = newChunk(pageSize, maxOrder, pageShifts, chunkSize); - long handle = c.allocate(normCapacity); - assert handle > 0; - c.initBuf(buf, handle, reqCapacity); + boolean success = c.allocate(buf, reqCapacity, normCapacity); + assert success; qInit.add(c); } @@ -263,7 +262,7 @@ private void allocateHuge(PooledByteBuf buf, int reqCapacity) { allocationsHuge.increment(); } - void free(PoolChunk chunk, long handle, int normCapacity, PoolThreadCache cache) { + void free(PoolChunk chunk, ByteBuffer nioBuffer, long handle, int normCapacity, PoolThreadCache cache) { if (chunk.unpooled) { int size = chunk.chunkSize(); destroyChunk(chunk); @@ -271,12 +270,12 @@ void free(PoolChunk chunk, long handle, int normCapacity, PoolThreadCache cac deallocationsHuge.increment(); } else { SizeClass sizeClass = sizeClass(normCapacity); - if (cache != null && cache.add(this, chunk, handle, normCapacity, sizeClass)) { + if (cache != null && cache.add(this, chunk, nioBuffer, handle, normCapacity, sizeClass)) { // cached so not free it. return; } - freeChunk(chunk, handle, sizeClass); + freeChunk(chunk, handle, sizeClass, nioBuffer); } } @@ -287,7 +286,7 @@ private SizeClass sizeClass(int normCapacity) { return isTiny(normCapacity) ? SizeClass.Tiny : SizeClass.Small; } - void freeChunk(PoolChunk chunk, long handle, SizeClass sizeClass) { + void freeChunk(PoolChunk chunk, long handle, SizeClass sizeClass, ByteBuffer nioBuffer) { final boolean destroyChunk; synchronized (this) { switch (sizeClass) { @@ -303,7 +302,7 @@ void freeChunk(PoolChunk chunk, long handle, SizeClass sizeClass) { default: throw new Error(); } - destroyChunk = !chunk.parent.free(chunk, handle); + destroyChunk = !chunk.parent.free(chunk, handle, nioBuffer); } if (destroyChunk) { // destroyChunk not need to be called while holding the synchronized lock. @@ -387,6 +386,7 @@ void reallocate(PooledByteBuf buf, int newCapacity, boolean freeOldMemory) { } PoolChunk oldChunk = buf.chunk; + ByteBuffer oldNioBuffer = buf.tmpNioBuf; long oldHandle = buf.handle; T oldMemory = buf.memory; int oldOffset = buf.offset; @@ -415,7 +415,7 @@ void reallocate(PooledByteBuf buf, int newCapacity, boolean freeOldMemory) { buf.setIndex(readerIndex, writerIndex); if (freeOldMemory) { - free(oldChunk, oldHandle, oldMaxLength, buf.cache); + free(oldChunk, oldNioBuffer, oldHandle, oldMaxLength, buf.cache); } } diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunk.java b/buffer/src/main/java/io/netty/buffer/PoolChunk.java index 93eda84104bf..0a19a5a1d492 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolChunk.java +++ b/buffer/src/main/java/io/netty/buffer/PoolChunk.java @@ -16,6 +16,10 @@ package io.netty.buffer; +import java.nio.ByteBuffer; +import java.util.ArrayDeque; +import java.util.Deque; + /** * Description of algorithm for PageRun/PoolSubpage allocation from PoolChunk * @@ -107,7 +111,6 @@ final class PoolChunk implements PoolChunkMetric { final T memory; final boolean unpooled; final int offset; - private final byte[] memoryMap; private final byte[] depthMap; private final PoolSubpage[] subpages; @@ -122,6 +125,13 @@ final class PoolChunk implements PoolChunkMetric { /** Used to mark memory as unusable */ private final byte unusable; + // Use as cache for ByteBuffer created from the memory. These are just duplicates and so are only a container + // around the memory itself. These are often needed for operations within the Pooled*ByteBuf and so + // may produce extra GC, which can be greatly reduced by caching the duplicates. + // + // This may be null if the PoolChunk is unpooled as pooling the ByteBuffer instances does not make any sense here. + private final Deque cachedNioBuffers; + private int freeBytes; PoolChunkList parent; @@ -163,6 +173,7 @@ final class PoolChunk implements PoolChunkMetric { } subpages = newSubpageArray(maxSubpageAllocs); + cachedNioBuffers = new ArrayDeque(8); } /** Creates a special chunk that is not pooled. */ @@ -182,6 +193,7 @@ final class PoolChunk implements PoolChunkMetric { chunkSize = size; log2ChunkSize = log2(chunkSize); maxSubpageAllocs = 0; + cachedNioBuffers = null; } @SuppressWarnings("unchecked") @@ -210,12 +222,20 @@ private int usage(int freeBytes) { return 100 - freePercentage; } - long allocate(int normCapacity) { + boolean allocate(PooledByteBuf buf, int reqCapacity, int normCapacity) { + final long handle; if ((normCapacity & subpageOverflowMask) != 0) { // >= pageSize - return allocateRun(normCapacity); + handle = allocateRun(normCapacity); } else { - return allocateSubpage(normCapacity); + handle = allocateSubpage(normCapacity); } + + if (handle < 0) { + return false; + } + ByteBuffer nioBuffer = cachedNioBuffers != null ? cachedNioBuffers.pollLast() : null; + initBuf(buf, nioBuffer, handle, reqCapacity); + return true; } /** @@ -310,8 +330,8 @@ private long allocateRun(int normCapacity) { } /** - * Create/ initialize a new PoolSubpage of normCapacity - * Any PoolSubpage created/ initialized here is added to subpage pool in the PoolArena that owns this PoolChunk + * Create / initialize a new PoolSubpage of normCapacity + * Any PoolSubpage created / initialized here is added to subpage pool in the PoolArena that owns this PoolChunk * * @param normCapacity normalized capacity * @return index in memoryMap @@ -320,8 +340,8 @@ private long allocateSubpage(int normCapacity) { // Obtain the head of the PoolSubPage pool that is owned by the PoolArena and synchronize on it. // This is need as we may add it back and so alter the linked-list structure. PoolSubpage head = arena.findSubpagePoolHead(normCapacity); + int d = maxOrder; // subpages are only be allocated from pages i.e., leaves synchronized (head) { - int d = maxOrder; // subpages are only be allocated from pages i.e., leaves int id = allocateNode(d); if (id < 0) { return id; @@ -352,7 +372,7 @@ private long allocateSubpage(int normCapacity) { * * @param handle handle to free */ - void free(long handle) { + void free(long handle, ByteBuffer nioBuffer) { int memoryMapIdx = memoryMapIdx(handle); int bitmapIdx = bitmapIdx(handle); @@ -372,26 +392,32 @@ void free(long handle) { freeBytes += runLength(memoryMapIdx); setValue(memoryMapIdx, depth(memoryMapIdx)); updateParentsFree(memoryMapIdx); + + if (nioBuffer != null && cachedNioBuffers != null && + cachedNioBuffers.size() < PooledByteBufAllocator.DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK) { + cachedNioBuffers.offer(nioBuffer); + } } - void initBuf(PooledByteBuf buf, long handle, int reqCapacity) { + void initBuf(PooledByteBuf buf, ByteBuffer nioBuffer, long handle, int reqCapacity) { int memoryMapIdx = memoryMapIdx(handle); int bitmapIdx = bitmapIdx(handle); if (bitmapIdx == 0) { byte val = value(memoryMapIdx); assert val == unusable : String.valueOf(val); - buf.init(this, handle, runOffset(memoryMapIdx) + offset, reqCapacity, runLength(memoryMapIdx), - arena.parent.threadCache()); + buf.init(this, nioBuffer, handle, runOffset(memoryMapIdx) + offset, + reqCapacity, runLength(memoryMapIdx), arena.parent.threadCache()); } else { - initBufWithSubpage(buf, handle, bitmapIdx, reqCapacity); + initBufWithSubpage(buf, nioBuffer, handle, bitmapIdx, reqCapacity); } } - void initBufWithSubpage(PooledByteBuf buf, long handle, int reqCapacity) { - initBufWithSubpage(buf, handle, bitmapIdx(handle), reqCapacity); + void initBufWithSubpage(PooledByteBuf buf, ByteBuffer nioBuffer, long handle, int reqCapacity) { + initBufWithSubpage(buf, nioBuffer, handle, bitmapIdx(handle), reqCapacity); } - private void initBufWithSubpage(PooledByteBuf buf, long handle, int bitmapIdx, int reqCapacity) { + private void initBufWithSubpage(PooledByteBuf buf, ByteBuffer nioBuffer, + long handle, int bitmapIdx, int reqCapacity) { assert bitmapIdx != 0; int memoryMapIdx = memoryMapIdx(handle); @@ -401,7 +427,7 @@ private void initBufWithSubpage(PooledByteBuf buf, long handle, int bitmapIdx assert reqCapacity <= subpage.elemSize; buf.init( - this, handle, + this, nioBuffer, handle, runOffset(memoryMapIdx) + (bitmapIdx & 0x3FFFFFFF) * subpage.elemSize + offset, reqCapacity, subpage.elemSize, arena.parent.threadCache()); } diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunkList.java b/buffer/src/main/java/io/netty/buffer/PoolChunkList.java index f92834d85c4f..e610be8e9b1a 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolChunkList.java +++ b/buffer/src/main/java/io/netty/buffer/PoolChunkList.java @@ -25,6 +25,8 @@ import static java.lang.Math.*; +import java.nio.ByteBuffer; + final class PoolChunkList implements PoolChunkListMetric { private static final Iterator EMPTY_METRICS = Collections.emptyList().iterator(); private final PoolArena arena; @@ -75,21 +77,14 @@ void prevList(PoolChunkList prevList) { } boolean allocate(PooledByteBuf buf, int reqCapacity, int normCapacity) { - if (head == null || normCapacity > maxCapacity) { + if (normCapacity > maxCapacity) { // Either this PoolChunkList is empty or the requested capacity is larger then the capacity which can // be handled by the PoolChunks that are contained in this PoolChunkList. return false; } - for (PoolChunk cur = head;;) { - long handle = cur.allocate(normCapacity); - if (handle < 0) { - cur = cur.next; - if (cur == null) { - return false; - } - } else { - cur.initBuf(buf, handle, reqCapacity); + for (PoolChunk cur = head; cur != null; cur = cur.next) { + if (cur.allocate(buf, reqCapacity, normCapacity)) { if (cur.usage() >= maxUsage) { remove(cur); nextList.add(cur); @@ -97,10 +92,11 @@ boolean allocate(PooledByteBuf buf, int reqCapacity, int normCapacity) { return true; } } + return false; } - boolean free(PoolChunk chunk, long handle) { - chunk.free(handle); + boolean free(PoolChunk chunk, long handle, ByteBuffer nioBuffer) { + chunk.free(handle, nioBuffer); if (chunk.usage() < minUsage) { remove(chunk); // Move the PoolChunk down the PoolChunkList linked-list. diff --git a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java index 2ad397755edc..de2a9be0604c 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java +++ b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java @@ -200,12 +200,13 @@ private boolean allocate(MemoryRegionCache cache, PooledByteBuf buf, int reqC * Returns {@code true} if it fit into the cache {@code false} otherwise. */ @SuppressWarnings({ "unchecked", "rawtypes" }) - boolean add(PoolArena area, PoolChunk chunk, long handle, int normCapacity, SizeClass sizeClass) { + boolean add(PoolArena area, PoolChunk chunk, ByteBuffer nioBuffer, + long handle, int normCapacity, SizeClass sizeClass) { MemoryRegionCache cache = cache(area, normCapacity, sizeClass); if (cache == null) { return false; } - return cache.add(chunk, handle); + return cache.add(chunk, nioBuffer, handle); } private MemoryRegionCache cache(PoolArena area, int normCapacity, SizeClass sizeClass) { @@ -346,8 +347,8 @@ private static final class SubPageMemoryRegionCache extends MemoryRegionCache @Override protected void initBuf( - PoolChunk chunk, long handle, PooledByteBuf buf, int reqCapacity) { - chunk.initBufWithSubpage(buf, handle, reqCapacity); + PoolChunk chunk, ByteBuffer nioBuffer, long handle, PooledByteBuf buf, int reqCapacity) { + chunk.initBufWithSubpage(buf, nioBuffer, handle, reqCapacity); } } @@ -361,8 +362,8 @@ private static final class NormalMemoryRegionCache extends MemoryRegionCache< @Override protected void initBuf( - PoolChunk chunk, long handle, PooledByteBuf buf, int reqCapacity) { - chunk.initBuf(buf, handle, reqCapacity); + PoolChunk chunk, ByteBuffer nioBuffer, long handle, PooledByteBuf buf, int reqCapacity) { + chunk.initBuf(buf, nioBuffer, handle, reqCapacity); } } @@ -381,15 +382,15 @@ private abstract static class MemoryRegionCache { /** * Init the {@link PooledByteBuf} using the provided chunk and handle with the capacity restrictions. */ - protected abstract void initBuf(PoolChunk chunk, long handle, + protected abstract void initBuf(PoolChunk chunk, ByteBuffer nioBuffer, long handle, PooledByteBuf buf, int reqCapacity); /** * Add to cache if not already full. */ @SuppressWarnings("unchecked") - public final boolean add(PoolChunk chunk, long handle) { - Entry entry = newEntry(chunk, handle); + public final boolean add(PoolChunk chunk, ByteBuffer nioBuffer, long handle) { + Entry entry = newEntry(chunk, nioBuffer, handle); boolean queued = queue.offer(entry); if (!queued) { // If it was not possible to cache the chunk, immediately recycle the entry @@ -407,7 +408,7 @@ public final boolean allocate(PooledByteBuf buf, int reqCapacity) { if (entry == null) { return false; } - initBuf(entry.chunk, entry.handle, buf, reqCapacity); + initBuf(entry.chunk, entry.nioBuffer, entry.handle, buf, reqCapacity); entry.recycle(); // allocations is not thread-safe which is fine as this is only called from the same thread all time. @@ -453,16 +454,18 @@ public final void trim() { private void freeEntry(Entry entry) { PoolChunk chunk = entry.chunk; long handle = entry.handle; + ByteBuffer nioBuffer = entry.nioBuffer; // recycle now so PoolChunk can be GC'ed. entry.recycle(); - chunk.arena.freeChunk(chunk, handle, sizeClass); + chunk.arena.freeChunk(chunk, handle, sizeClass, nioBuffer); } static final class Entry { final Handle> recyclerHandle; PoolChunk chunk; + ByteBuffer nioBuffer; long handle = -1; Entry(Handle> recyclerHandle) { @@ -471,15 +474,17 @@ static final class Entry { void recycle() { chunk = null; + nioBuffer = null; handle = -1; recyclerHandle.recycle(this); } } @SuppressWarnings("rawtypes") - private static Entry newEntry(PoolChunk chunk, long handle) { + private static Entry newEntry(PoolChunk chunk, ByteBuffer nioBuffer, long handle) { Entry entry = RECYCLER.get(); entry.chunk = chunk; + entry.nioBuffer = nioBuffer; entry.handle = handle; return entry; } diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java index 56a4be387232..beffbb07ee5a 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java @@ -33,7 +33,7 @@ abstract class PooledByteBuf extends AbstractReferenceCountedByteBuf { protected int length; int maxLength; PoolThreadCache cache; - private ByteBuffer tmpNioBuf; + ByteBuffer tmpNioBuf; private ByteBufAllocator allocator; @SuppressWarnings("unchecked") @@ -42,27 +42,29 @@ protected PooledByteBuf(Recycler.Handle> recyclerHand this.recyclerHandle = (Handle>) recyclerHandle; } - void init(PoolChunk chunk, long handle, int offset, int length, int maxLength, PoolThreadCache cache) { - init0(chunk, handle, offset, length, maxLength, cache); + void init(PoolChunk chunk, ByteBuffer nioBuffer, + long handle, int offset, int length, int maxLength, PoolThreadCache cache) { + init0(chunk, nioBuffer, handle, offset, length, maxLength, cache); } void initUnpooled(PoolChunk chunk, int length) { - init0(chunk, 0, chunk.offset, length, length, null); + init0(chunk, null, 0, chunk.offset, length, length, null); } - private void init0(PoolChunk chunk, long handle, int offset, int length, int maxLength, PoolThreadCache cache) { + private void init0(PoolChunk chunk, ByteBuffer nioBuffer, + long handle, int offset, int length, int maxLength, PoolThreadCache cache) { assert handle >= 0; assert chunk != null; this.chunk = chunk; memory = chunk.memory; + tmpNioBuf = nioBuffer; allocator = chunk.arena.parent; this.cache = cache; this.handle = handle; this.offset = offset; this.length = length; this.maxLength = maxLength; - tmpNioBuf = null; } /** @@ -166,8 +168,8 @@ protected final void deallocate() { final long handle = this.handle; this.handle = -1; memory = null; + chunk.arena.free(chunk, tmpNioBuf, handle, maxLength, cache); tmpNioBuf = null; - chunk.arena.free(chunk, handle, maxLength, cache); chunk = null; recycle(); } diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java index aa9ee28b9484..de6eee1d54dc 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java @@ -45,6 +45,7 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements private static final int DEFAULT_CACHE_TRIM_INTERVAL; private static final boolean DEFAULT_USE_CACHE_FOR_ALL_THREADS; private static final int DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT; + static final int DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK; private static final int MIN_PAGE_SIZE = 4096; private static final int MAX_CHUNK_SIZE = (int) (((long) Integer.MAX_VALUE + 1) / 2); @@ -116,6 +117,11 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = SystemPropertyUtil.getInt( "io.netty.allocator.directMemoryCacheAlignment", 0); + // Use 1023 by default as we use an ArrayDeque as backing storage which will then allocate an internal array + // of 1024 elements. Otherwise we would allocate 2048 and only use 1024 which is wasteful. + DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK = SystemPropertyUtil.getInt( + "io.netty.allocator.maxCachedByteBuffersPerChunk", 1023); + if (logger.isDebugEnabled()) { logger.debug("-Dio.netty.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA); logger.debug("-Dio.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA); @@ -136,6 +142,8 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements logger.debug("-Dio.netty.allocator.maxCachedBufferCapacity: {}", DEFAULT_MAX_CACHED_BUFFER_CAPACITY); logger.debug("-Dio.netty.allocator.cacheTrimInterval: {}", DEFAULT_CACHE_TRIM_INTERVAL); logger.debug("-Dio.netty.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS); + logger.debug("-Dio.netty.allocator.maxCachedByteBuffersPerChunk: {}", + DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK); } } diff --git a/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java index 1dcc3702c4b7..e2dc22cb07db 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java @@ -49,9 +49,9 @@ private PooledUnsafeDirectByteBuf(Recycler.Handle rec } @Override - void init(PoolChunk chunk, long handle, int offset, int length, int maxLength, - PoolThreadCache cache) { - super.init(chunk, handle, offset, length, maxLength, cache); + void init(PoolChunk chunk, ByteBuffer nioBuffer, + long handle, int offset, int length, int maxLength, PoolThreadCache cache) { + super.init(chunk, nioBuffer, handle, offset, length, maxLength, cache); initMemoryAddress(); } From b8a3394f9b9e5abba80a26335f762795ee07b877 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Tue, 4 Dec 2018 15:46:25 +0100 Subject: [PATCH 297/417] Adding an execute burst cost benchmark for Netty executors (#8594) Motivation: Netty executors doesn't have yet any means to compare with each others nor to compare with the j.u.c. executors Modifications: A new benchmark measuring execute burst cost is being added Result: It's now possible to compare some of Netty executors with each others and with the j.u.c. executors --- microbench/pom.xml | 32 ++ .../BurstCostExecutorsBenchmark.java | 331 ++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 microbench/src/main/java/io/netty/microbench/concurrent/BurstCostExecutorsBenchmark.java diff --git a/microbench/pom.xml b/microbench/pom.xml index 6ae59054833c..e16df5d0ba95 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -35,6 +35,9 @@ + + @@ -61,6 +64,29 @@ + + mac + + + mac + + + + ${jni.classifier} + + + + + maven-compiler-plugin + + + **/*.java + + + + + + benchmark-jar @@ -132,6 +158,12 @@ ${project.version} ${epoll.classifier} + + ${project.groupId} + netty-transport-native-kqueue + ${project.version} + ${kqueue.classifier} + junit junit diff --git a/microbench/src/main/java/io/netty/microbench/concurrent/BurstCostExecutorsBenchmark.java b/microbench/src/main/java/io/netty/microbench/concurrent/BurstCostExecutorsBenchmark.java new file mode 100644 index 000000000000..11164d2f0b20 --- /dev/null +++ b/microbench/src/main/java/io/netty/microbench/concurrent/BurstCostExecutorsBenchmark.java @@ -0,0 +1,331 @@ +/* + * Copyright 2018 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: + * + * http://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.microbench.concurrent; + +import io.netty.channel.epoll.Epoll; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.kqueue.KQueue; +import io.netty.channel.kqueue.KQueueEventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.microbench.util.AbstractMicrobenchmark; +import io.netty.util.concurrent.DefaultEventExecutor; +import io.netty.util.internal.PlatformDependent; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.Collection; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +@State(Scope.Benchmark) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class BurstCostExecutorsBenchmark extends AbstractMicrobenchmark { + + /** + * This executor is useful as the best burst latency performer because it won't go to sleep and won't be hit by the + * cost of being awaken on both offer/consumer side. + */ + private static final class SpinExecutorService implements ExecutorService { + + private static final Runnable POISON_PILL = new Runnable() { + @Override + public void run() { + } + }; + private final Queue tasks; + private final AtomicBoolean poisoned = new AtomicBoolean(); + private final Thread executorThread; + + public SpinExecutorService(int maxTasks) { + tasks = PlatformDependent.newFixedMpscQueue(maxTasks); + executorThread = new Thread(new Runnable() { + @Override + public void run() { + final Queue tasks = SpinExecutorService.this.tasks; + Runnable task; + while ((task = tasks.poll()) != POISON_PILL) { + if (task != null) { + task.run(); + } + } + } + }); + executorThread.start(); + } + + @Override + public void shutdown() { + if (poisoned.compareAndSet(false, true)) { + while (!tasks.offer(POISON_PILL)) { + // Just try again + } + try { + executorThread.join(); + } catch (InterruptedException e) { + //We're quite trusty :) + } + } + } + + @Override + public List shutdownNow() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isShutdown() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isTerminated() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public Future submit(Callable task) { + throw new UnsupportedOperationException(); + } + + @Override + public Future submit(Runnable task, T result) { + throw new UnsupportedOperationException(); + } + + @Override + public Future submit(Runnable task) { + throw new UnsupportedOperationException(); + } + + @Override + public List> invokeAll(Collection> tasks) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public T invokeAny(Collection> tasks) + throws InterruptedException, ExecutionException { + throw new UnsupportedOperationException(); + } + + @Override + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + throw new UnsupportedOperationException(); + } + + @Override + public void execute(Runnable command) { + if (!tasks.offer(command)) { + throw new RejectedExecutionException( + "If that happens, there is something wrong with the available capacity/burst size"); + } + } + } + + private enum ExecutorType { + spinning, + defaultEventExecutor, + juc, + nioEventLoop, + epollEventLoop, + kqueueEventLoop + } + + @Param({ "1", "10" }) + private int burstLength; + @Param({ "spinning", "epollEventLoop", "nioEventLoop", "defaultEventExecutor", "juc", "kqueueEventLoop" }) + private String executorType; + @Param({ "0", "10" }) + private int work; + + private ExecutorService executor; + private ExecutorService executorToShutdown; + + @Setup + public void setup() { + ExecutorType type = ExecutorType.valueOf(executorType); + switch (type) { + case spinning: + //The case with 3 producers can have a peak of 3*burstLength offers: + //4 is to leave some room between the offers and 1024 is to leave some room + //between producer/consumer when work is > 0 and 1 producer. + //If work = 0 then the task queue is supposed to be near empty most of the time. + executor = new SpinExecutorService(Math.min(1024, burstLength * 4)); + executorToShutdown = executor; + break; + case defaultEventExecutor: + executor = new DefaultEventExecutor(); + executorToShutdown = executor; + break; + case juc: + executor = Executors.newSingleThreadScheduledExecutor(); + executorToShutdown = executor; + break; + case nioEventLoop: + NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1); + nioEventLoopGroup.setIoRatio(1); + executor = nioEventLoopGroup.next(); + executorToShutdown = nioEventLoopGroup; + break; + case epollEventLoop: + Epoll.ensureAvailability(); + EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(1); + epollEventLoopGroup.setIoRatio(1); + executor = epollEventLoopGroup.next(); + executorToShutdown = epollEventLoopGroup; + break; + case kqueueEventLoop: + KQueue.ensureAvailability(); + KQueueEventLoopGroup kQueueEventLoopGroup = new KQueueEventLoopGroup(1); + kQueueEventLoopGroup.setIoRatio(1); + executor = kQueueEventLoopGroup.next(); + executorToShutdown = kQueueEventLoopGroup; + break; + } + } + + @TearDown + public void tearDown() { + executorToShutdown.shutdown(); + } + + @State(Scope.Thread) + public static class PerThreadState { + //To reduce the benchmark noise we avoid using AtomicInteger that would + //suffer of false sharing while reading/writing the counter due to the surrounding + //instances on heap: thanks to JMH the "completed" field will be padded + //avoiding false-sharing for free + private static final AtomicIntegerFieldUpdater DONE_UPDATER = + AtomicIntegerFieldUpdater.newUpdater(PerThreadState.class, "completed"); + private volatile int completed; + + private Runnable completeTask; + + @Setup + public void setup(BurstCostExecutorsBenchmark bench) { + final int work = bench.work; + if (work > 0) { + completeTask = new Runnable() { + @Override + public void run() { + Blackhole.consumeCPU(work); + //We can avoid the full barrier cost of a volatile set given that the + //benchmark is focusing on executors with a single threaded consumer: + //it would reduce the cost on consumer side while allowing to focus just + //to the threads hand-off/wake-up cost + DONE_UPDATER.lazySet(PerThreadState.this, completed + 1); + } + }; + } else { + completeTask = new Runnable() { + @Override + public void run() { + //We can avoid the full barrier cost of a volatile set given that the + //benchmark is focusing on executors with a single threaded consumer: + //it would reduce the cost on consumer side while allowing to focus just + //to the threads hand-off/wake-up cost + DONE_UPDATER.lazySet(PerThreadState.this, completed + 1); + } + }; + } + } + + /** + * Single-writer reset of completed counter. + */ + public void resetCompleted() { + //We can avoid the full barrier cost of a volatile set given that + //the counter can be reset from a single thread and it should be reset + //only after any submitted tasks are completed + DONE_UPDATER.lazySet(this, 0); + } + + /** + * It would spin-wait until at least {@code value} tasks are being completed. + */ + public int spinWaitCompletionOf(int value) { + while (true) { + final int lastRead = this.completed; + if (lastRead >= value) { + return lastRead; + } + } + } + } + + @Benchmark + @BenchmarkMode(Mode.SampleTime) + @Threads(1) + public int test1Producer(final PerThreadState state) { + return executeBurst(state); + } + + @Benchmark + @BenchmarkMode(Mode.SampleTime) + @Threads(2) + public int test2Producers(final PerThreadState state) { + return executeBurst(state); + } + + @Benchmark + @BenchmarkMode(Mode.SampleTime) + @Threads(3) + public int test3Producers(final PerThreadState state) { + return executeBurst(state); + } + + private int executeBurst(final PerThreadState state) { + final ExecutorService executor = this.executor; + final int burstLength = this.burstLength; + final Runnable completeTask = state.completeTask; + for (int i = 0; i < burstLength; i++) { + executor.execute(completeTask); + } + final int value = state.spinWaitCompletionOf(burstLength); + state.resetCompleted(); + return value; + } +} From d0d30f1fbe2117cd0a7b2ad103cd3ff596a2d7f9 Mon Sep 17 00:00:00 2001 From: Nick Travers Date: Tue, 4 Dec 2018 23:42:23 -0800 Subject: [PATCH 298/417] Loosen bounds check on CompositeByteBuf's maxNumComponents (#8621) Motivation: In versions of Netty prior to 4.1.31.Final, a CompositeByteBuf could be created with any size (including potentially nonsensical negative values). This behavior changed in e7737b993, which introduced a bounds check to only allow for a component size greater than one. This broke some existing use cases that attempted to create a byte buf with a single component. Modifications: Lower the bounds check on numComponents to include the single component case, but still throw an exception for anything less than one. Add unit tests for the case of numComponents being less than, equal to, and greater than this lower bound. Result: Return to the behavior of 4.1.30.Final, allowing one component, but still include an explicit check against a lower bound. Note that while creating a CompositeByteBuf with a single component is in some ways a contradiction of the term "composite", this patch caters for existing uses while excluding the clearly nonsensical case of asking for a CompositeByteBuf with zero or fewer components. Fixes #8613. --- .../io/netty/buffer/CompositeByteBuf.java | 4 +-- .../buffer/AbstractCompositeByteBufTest.java | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index c9212d5c6e7e..ce3c9f7f41ea 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -64,9 +64,9 @@ private CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumCompo if (alloc == null) { throw new NullPointerException("alloc"); } - if (maxNumComponents < 2) { + if (maxNumComponents < 1) { throw new IllegalArgumentException( - "maxNumComponents: " + maxNumComponents + " (expected: >= 2)"); + "maxNumComponents: " + maxNumComponents + " (expected: >= 1)"); } this.alloc = alloc; this.direct = direct; diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java index 8e0f148cdf0d..c51a991ec370 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java @@ -52,6 +52,8 @@ */ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { + private static final ByteBufAllocator ALLOC = UnpooledByteBufAllocator.DEFAULT; + private final ByteOrder order; protected AbstractCompositeByteBufTest(ByteOrder order) { @@ -1262,4 +1264,35 @@ private static void testDecompose(int offset, int length, int expectedListSize) } } + @Test + public void testComponentsLessThanLowerBound() { + try { + new CompositeByteBuf(ALLOC, true, 0); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("maxNumComponents: 0 (expected: >= 1)", e.getMessage()); + } + } + + @Test + public void testComponentsEqualToLowerBound() { + assertCompositeBufCreated(1); + } + + @Test + public void testComponentsGreaterThanLowerBound() { + assertCompositeBufCreated(5); + } + + /** + * Assert that a new {@linkplain CompositeByteBuf} was created successfully with the desired number of max + * components. + */ + private static void assertCompositeBufCreated(int expectedMaxComponents) { + CompositeByteBuf buf = new CompositeByteBuf(ALLOC, true, expectedMaxComponents); + + assertEquals(expectedMaxComponents, buf.maxNumComponents()); + assertTrue(buf.release()); + } + } From 9f9aa1ae01e32fbe22caf8fe3939c12ce8f6d7ba Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 5 Dec 2018 15:29:33 +0100 Subject: [PATCH 299/417] Respect ctx.read() calls while processing reads for the child channels when using the Http2MultiplexCodec. (#8617) Motivation: We did not correct respect ctx.read() calls while processing a read for a child Channel. This could lead to read stales when auto read is disabled and no other read was requested. Modifications: - Keep track of extra read() calls while processing reads - Add unit tests that verify that read() is respected when triggered either in channelRead(...) or channelReadComplete(...) Result: Fixes https://github.com/netty/netty/issues/8209. --- .../codec/http2/Http2MultiplexCodec.java | 61 +++++++++++++++---- .../codec/http2/Http2MultiplexCodecTest.java | 54 ++++++++++++++++ 2 files changed, 102 insertions(+), 13 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java index a7ca0bb83114..6de1dd2df910 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java @@ -446,6 +446,26 @@ private boolean initialWritability(DefaultHttp2FrameStream stream) { return !isStreamIdValid(stream.id()) || isWritable(stream); } + /** + * The current status of the read-processing for a {@link Http2StreamChannel}. + */ + private enum ReadStatus { + /** + * No read in progress and no read was requested (yet) + */ + IDLE, + + /** + * Reading in progress + */ + IN_PROGRESS, + + /** + * A read operation was requested. + */ + REQUESTED + } + // TODO: Handle writability changes due writing from outside the eventloop. private final class DefaultHttp2StreamChannel extends DefaultAttributeMap implements Http2StreamChannel { private final Http2StreamChannelConfig config = new Http2StreamChannelConfig(this); @@ -461,13 +481,15 @@ private final class DefaultHttp2StreamChannel extends DefaultAttributeMap implem private volatile boolean writable; private boolean outboundClosed; + /** - * This variable represents if a read is in progress for the current channel. Note that depending upon the - * {@link RecvByteBufAllocator} behavior a read may extend beyond the {@link Http2ChannelUnsafe#beginRead()} - * method scope. The {@link Http2ChannelUnsafe#beginRead()} loop may drain all pending data, and then if the - * parent channel is reading this channel may still accept frames. + * This variable represents if a read is in progress for the current channel or was requested. + * Note that depending upon the {@link RecvByteBufAllocator} behavior a read may extend beyond the + * {@link Http2ChannelUnsafe#beginRead()} method scope. The {@link Http2ChannelUnsafe#beginRead()} loop may + * drain all pending data, and then if the parent channel is reading this channel may still accept frames. */ - private boolean readInProgress; + private ReadStatus readStatus = ReadStatus.IDLE; + private Queue inboundBuffer; /** {@code true} after the first HEADERS frame has been written **/ @@ -757,9 +779,9 @@ void fireChildRead(Http2Frame frame) { assert eventLoop().inEventLoop(); if (!isActive()) { ReferenceCountUtil.release(frame); - } else if (readInProgress) { - // If readInProgress there cannot be anything in the queue, otherwise we would have drained it from the - // queue and processed it during the read cycle. + } else if (readStatus != ReadStatus.IDLE) { + // If a read is in progress or has been requested, there cannot be anything in the queue, + // otherwise we would have drained it from the queue and processed it during the read cycle. assert inboundBuffer == null || inboundBuffer.isEmpty(); final Handle allocHandle = unsafe.recvBufAllocHandle(); unsafe.doRead0(frame, allocHandle); @@ -783,7 +805,7 @@ void fireChildRead(Http2Frame frame) { void fireChildReadComplete() { assert eventLoop().inEventLoop(); - assert readInProgress; + assert readStatus == ReadStatus.IN_PROGRESS; unsafe.notifyReadComplete(unsafe.recvBufAllocHandle()); } @@ -985,11 +1007,20 @@ private void invokeLater(Runnable task) { @Override public void beginRead() { - if (readInProgress || !isActive()) { + if (!isActive()) { return; } - readInProgress = true; - doBeginRead(); + switch (readStatus) { + case IDLE: + readStatus = ReadStatus.IN_PROGRESS; + doBeginRead(); + break; + case IN_PROGRESS: + readStatus = ReadStatus.REQUESTED; + break; + default: + break; + } } void doBeginRead() { @@ -1026,7 +1057,11 @@ void readEOS() { void notifyReadComplete(Handle allocHandle) { assert next == null && previous == null; - readInProgress = false; + if (readStatus == ReadStatus.REQUESTED) { + readStatus = ReadStatus.IN_PROGRESS; + } else { + readStatus = ReadStatus.IDLE; + } allocHandle.readComplete(); pipeline().fireChannelReadComplete(); // Reading data may result in frames being written (e.g. WINDOW_UPDATE, RST, etc..). If the parent diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java index f09d55e91656..d857cf8d1e6f 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java @@ -260,6 +260,60 @@ public void channelReadShouldRespectAutoRead() { verifyFramesMultiplexedToCorrectChannel(inboundStream, inboundHandler, 2); } + @Test + public void readInChannelReadWithoutAutoRead() { + useReadWithoutAutoRead(false); + } + + @Test + public void readInChannelReadCompleteWithoutAutoRead() { + useReadWithoutAutoRead(true); + } + + private void useReadWithoutAutoRead(final boolean readComplete) { + LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); + Channel childChannel = inboundHandler.channel(); + assertTrue(childChannel.config().isAutoRead()); + childChannel.config().setAutoRead(false); + assertFalse(childChannel.config().isAutoRead()); + + Http2HeadersFrame headersFrame = inboundHandler.readInbound(); + assertNotNull(headersFrame); + + // Add a handler which will request reads. + childChannel.pipeline().addFirst(new ChannelInboundHandlerAdapter() { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + ctx.fireChannelRead(msg); + if (!readComplete) { + ctx.read(); + } + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + ctx.fireChannelReadComplete(); + if (readComplete) { + ctx.read(); + } + } + }); + + codec.onHttp2Frame( + new DefaultHttp2DataFrame(bb("hello world"), false).stream(inboundStream)); + codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("foo"), false).stream(inboundStream)); + codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("bar"), true).stream(inboundStream)); + codec.onChannelReadComplete(); + + codec.onHttp2Frame( + new DefaultHttp2DataFrame(bb("hello world"), false).stream(inboundStream)); + codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("foo"), false).stream(inboundStream)); + codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("bar"), true).stream(inboundStream)); + codec.onChannelReadComplete(); + + verifyFramesMultiplexedToCorrectChannel(inboundStream, inboundHandler, 6); + } + private Http2StreamChannel newOutboundStream() { return new Http2StreamChannelBootstrap(parentChannel).handler(childChannelInitializer) .open().syncUninterruptibly().getNow(); From 6739755d3901ee86e0492c0954f9c45b07142ae2 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 5 Dec 2018 15:31:21 +0100 Subject: [PATCH 300/417] =?UTF-8?q?NioEventLoop.register(...)=20should=20o?= =?UTF-8?q?ffload=20to=20the=20EventLoop=20if=20not=20alr=E2=80=A6=20(#861?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: java.nio.channels.spi.AbstractSelectableChannel.register(...) need to obtain multiple locks during execution which may produce a long wait time if we currently select. This lead to multiple CI failures in the past. Modifications: Ensure the register call takes place on the EventLoop. Result: No more flacky CI test timeouts. --- .../io/netty/channel/nio/NioEventLoop.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index 539ac81d37d0..16695c8f7e52 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -33,8 +33,9 @@ import java.lang.reflect.Field; import java.nio.channels.CancelledKeyException; import java.nio.channels.SelectableChannel; -import java.nio.channels.SelectionKey; import java.nio.channels.Selector; +import java.nio.channels.SelectionKey; + import java.nio.channels.spi.SelectorProvider; import java.security.AccessController; import java.security.PrivilegedAction; @@ -293,6 +294,26 @@ public void register(final SelectableChannel ch, final int interestOps, final Ni throw new IllegalStateException("event loop shut down"); } + if (inEventLoop()) { + register0(ch, interestOps, task); + } else { + try { + // Offload to the EventLoop as otherwise java.nio.channels.spi.AbstractSelectableChannel.register + // may block for a long time while trying to obtain an internal lock that may be hold while selecting. + submit(new Runnable() { + @Override + public void run() { + register0(ch, interestOps, task); + } + }).sync(); + } catch (InterruptedException ignore) { + // Even if interrupted we did schedule it so just mark the Thread as interrupted. + Thread.currentThread().interrupt(); + } + } + } + + private void register0(SelectableChannel ch, int interestOps, NioTask task) { try { ch.register(unwrappedSelector, interestOps, task); } catch (Exception e) { From 8331248671b9c0ea07cf8dbdfa5d8d2f89fdf459 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 5 Dec 2018 19:30:17 +0100 Subject: [PATCH 301/417] ChannelInitializer may be invoked multiple times when used with custom EventExecutor. (#8620) Motivation: The ChannelInitializer may be invoked multipled times when used with a custom EventExecutor as removal operation may be done asynchronously. We need to guard against this. Modifications: - Change Map to Set which is more correct in terms of how we use it. - Ensure we only modify the internal Set when the handler was removed yet - Add unit test. Result: Fixes https://github.com/netty/netty/issues/8616. --- .../io/netty/channel/ChannelInitializer.java | 32 ++++- .../netty/channel/ChannelInitializerTest.java | 126 ++++++++++++++++++ 2 files changed, 151 insertions(+), 7 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/ChannelInitializer.java b/transport/src/main/java/io/netty/channel/ChannelInitializer.java index 9ea1b1822190..18344d200fa5 100644 --- a/transport/src/main/java/io/netty/channel/ChannelInitializer.java +++ b/transport/src/main/java/io/netty/channel/ChannelInitializer.java @@ -18,11 +18,12 @@ import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelHandler.Sharable; -import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; -import java.util.concurrent.ConcurrentMap; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * A special {@link ChannelInboundHandler} which offers an easy way to initialize a {@link Channel} once it was @@ -53,9 +54,10 @@ public abstract class ChannelInitializer extends ChannelInboundHandlerAdapter { private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelInitializer.class); - // We use a ConcurrentMap as a ChannelInitializer is usually shared between all Channels in a Bootstrap / + // We use a Set as a ChannelInitializer is usually shared between all Channels in a Bootstrap / // ServerBootstrap. This way we can reduce the memory usage compared to use Attributes. - private final ConcurrentMap initMap = PlatformDependent.newConcurrentHashMap(); + private final Set initMap = Collections.newSetFromMap( + new ConcurrentHashMap()); /** * This method will be called once the {@link Channel} was registered. After the method returns this instance @@ -108,9 +110,14 @@ public void handlerAdded(ChannelHandlerContext ctx) throws Exception { } } + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + initMap.remove(ctx); + } + @SuppressWarnings("unchecked") private boolean initChannel(ChannelHandlerContext ctx) throws Exception { - if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance. + if (initMap.add(ctx)) { // Guard against re-entrance. try { initChannel((C) ctx.channel()); } catch (Throwable cause) { @@ -125,14 +132,25 @@ private boolean initChannel(ChannelHandlerContext ctx) throws Exception { return false; } - private void remove(ChannelHandlerContext ctx) { + private void remove(final ChannelHandlerContext ctx) { try { ChannelPipeline pipeline = ctx.pipeline(); if (pipeline.context(this) != null) { pipeline.remove(this); } } finally { - initMap.remove(ctx); + // The removal may happen in an async fashion if the EventExecutor we use does something funky. + if (ctx.isRemoved()) { + initMap.remove(ctx); + } else { + // Ensure we always remove from the Map in all cases to not produce a memory leak. + ctx.channel().closeFuture().addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) { + initMap.remove(ctx); + } + }); + } } } } diff --git a/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java b/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java index 26b5e4e9fcf4..2ac1bcdefa9f 100644 --- a/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java +++ b/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java @@ -21,12 +21,16 @@ import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; import io.netty.channel.local.LocalServerChannel; +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Future; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.util.Iterator; import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -35,6 +39,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; public class ChannelInitializerTest { @@ -249,6 +254,127 @@ private void testChannelRegisteredEventPropagation(ChannelInitializer errorRef = new AtomicReference(); + LocalAddress addr = new LocalAddress("test"); + + final EventExecutor executor = new DefaultEventLoop() { + private final ScheduledExecutorService execService = Executors.newSingleThreadScheduledExecutor(); + + @Override + public void shutdown() { + execService.shutdown(); + } + + @Override + public boolean inEventLoop(Thread thread) { + // Always return false which will ensure we always call execute(...) + return false; + } + + @Override + public boolean isShuttingDown() { + return false; + } + + @Override + public Future shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { + throw new IllegalStateException(); + } + + @Override + public Future terminationFuture() { + throw new IllegalStateException(); + } + + @Override + public boolean isShutdown() { + return execService.isShutdown(); + } + + @Override + public boolean isTerminated() { + return execService.isTerminated(); + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return execService.awaitTermination(timeout, unit); + } + + @Override + public void execute(Runnable command) { + execService.execute(command); + } + }; + + ServerBootstrap serverBootstrap = new ServerBootstrap() + .channel(LocalServerChannel.class) + .group(group) + .localAddress(addr) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(LocalChannel ch) { + ch.pipeline().addLast(executor, new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + invokeCount.incrementAndGet(); + ChannelHandlerContext ctx = ch.pipeline().context(this); + assertNotNull(ctx); + ch.pipeline().addAfter(ctx.executor(), + ctx.name(), null, new ChannelInboundHandlerAdapter() { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + // just drop on the floor. + } + }); + completeCount.incrementAndGet(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + errorRef.set(cause); + } + }); + } + }); + + Channel server = serverBootstrap.bind().sync().channel(); + + Bootstrap clientBootstrap = new Bootstrap() + .channel(LocalChannel.class) + .group(group) + .remoteAddress(addr) + .handler(new ChannelInboundHandlerAdapter()); + + Channel client = clientBootstrap.connect().sync().channel(); + client.writeAndFlush("Hello World").sync(); + + client.close().sync(); + server.close().sync(); + + client.closeFuture().sync(); + server.closeFuture().sync(); + + // Give some time to execute everything that was submitted before. + Thread.sleep(1000); + + executor.shutdown(); + assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS)); + + assertEquals(invokeCount.get(), 1); + assertEquals(invokeCount.get(), completeCount.get()); + + Throwable cause = errorRef.get(); + if (cause != null) { + throw cause; + } + } + private static void closeChannel(Channel c) { if (c != null) { c.close().syncUninterruptibly(); From 5df235c08391240e8106aad48a63323cf1a8b181 Mon Sep 17 00:00:00 2001 From: Feri73 Date: Thu, 6 Dec 2018 00:01:14 -0800 Subject: [PATCH 302/417] Correcting Maven Dependencies (#8622) Motivation: Most of the maven modules do not explicitly declare their dependencies and rely on transitivity, which is not always correct. Modifications: For all maven modules, add all of their dependencies to pom.xml Result: All of the (essentially non-transitive) depepdencies of the modules are explicitly declared in pom.xml --- codec-dns/pom.xml | 15 +++++++++++++ codec-haproxy/pom.xml | 10 +++++++++ codec-http/pom.xml | 15 +++++++++++++ codec-http2/pom.xml | 25 ++++++++++++++++++++++ codec-memcache/pom.xml | 15 +++++++++++++ codec-mqtt/pom.xml | 15 +++++++++++++ codec-redis/pom.xml | 15 +++++++++++++ codec-smtp/pom.xml | 15 +++++++++++++ codec-socks/pom.xml | 15 +++++++++++++ codec-stomp/pom.xml | 15 +++++++++++++ codec-xml/pom.xml | 9 ++++++-- codec/pom.xml | 10 +++++++++ example/pom.xml | 18 +++++++++++++++- handler-proxy/pom.xml | 16 ++++++++++++++ handler/pom.xml | 5 +++++ resolver-dns/pom.xml | 19 ++++++++++++++-- testsuite-autobahn/pom.xml | 15 +++++++++++++ testsuite-http2/pom.xml | 20 +++++++++++++++++ testsuite/pom.xml | 15 +++++++++++++ transport-native-epoll/pom.xml | 4 ++-- transport-native-kqueue/pom.xml | 4 ++-- transport-native-unix-common-tests/pom.xml | 5 +++++ transport-native-unix-common/pom.xml | 5 +++++ transport-sctp/pom.xml | 5 +++++ transport-udt/pom.xml | 8 +++++-- transport/pom.xml | 5 +++++ 26 files changed, 307 insertions(+), 11 deletions(-) diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 11a34dce94e0..2e950d349b20 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -33,6 +33,21 @@ + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + ${project.groupId} netty-codec diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index a73897327b0b..dde688ba7c0c 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -33,6 +33,16 @@ + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + ${project.groupId} netty-codec diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 768e1783aaac..d82f7f534195 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -33,6 +33,21 @@ + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + ${project.groupId} netty-codec diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 2f2609d5a220..92e2329f9ca5 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -33,6 +33,31 @@ + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + + + ${project.groupId} + netty-codec + ${project.version} + + + ${project.groupId} + netty-handler + ${project.version} + ${project.groupId} netty-codec-http diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 9a20defb5d18..65b453c20028 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -33,6 +33,21 @@ + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + ${project.groupId} netty-codec diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 85c72fb66b06..65db75defc6e 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -33,6 +33,21 @@ + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + ${project.groupId} netty-codec diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index d8f80e555a34..d3d1140f04da 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -33,6 +33,21 @@ + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + ${project.groupId} netty-codec diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index ce59f5fcdd17..ca591e11ab8e 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -33,6 +33,21 @@ + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + ${project.groupId} netty-codec diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 2d6ce44674b3..8498e63d519f 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -33,6 +33,21 @@ + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + ${project.groupId} netty-codec diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 63a298df92d9..dc44509c3348 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -33,6 +33,21 @@ + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + ${project.groupId} netty-codec diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index 0c76e7033204..c5d130de35f5 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -35,12 +35,17 @@ ${project.groupId} - netty-codec + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport ${project.version} ${project.groupId} - netty-handler + netty-codec ${project.version} diff --git a/codec/pom.xml b/codec/pom.xml index c26b6e0a9b1a..846c14520d4e 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -33,6 +33,16 @@ + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + ${project.groupId} netty-transport diff --git a/example/pom.xml b/example/pom.xml index 4174c9cefd45..2f727ac3e310 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -30,6 +30,16 @@ Netty/Example + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + ${project.groupId} netty-transport @@ -37,7 +47,7 @@ ${project.groupId} - netty-transport-sctp + netty-codec ${project.version} @@ -45,6 +55,11 @@ netty-handler ${project.version} + + ${project.groupId} + netty-transport-sctp + ${project.version} + ${project.groupId} netty-handler-proxy @@ -80,6 +95,7 @@ netty-codec-stomp ${project.version} + com.google.protobuf protobuf-java diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 279b4100ed65..07dce585a7ce 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -35,11 +35,26 @@ Netty/Handler/Proxy + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + ${project.groupId} netty-transport ${project.version} + + ${project.groupId} + netty-codec + ${project.version} + ${project.groupId} netty-codec-socks @@ -50,6 +65,7 @@ netty-codec-http ${project.version} + ${project.groupId} netty-handler diff --git a/handler/pom.xml b/handler/pom.xml index 6318eb1fe67a..1be8fad02f0a 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -35,6 +35,11 @@ Netty/Handler + + ${project.groupId} + netty-common + ${project.version} + ${project.groupId} netty-buffer diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index 3d48f9055516..bd4237ea5cca 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -35,12 +35,17 @@ ${project.groupId} - netty-resolver + netty-common ${project.version} ${project.groupId} - netty-codec-dns + netty-buffer + ${project.version} + + + ${project.groupId} + netty-resolver ${project.version} @@ -48,6 +53,16 @@ netty-transport ${project.version} + + ${project.groupId} + netty-codec + ${project.version} + + + ${project.groupId} + netty-codec-dns + ${project.version} + org.apache.directory.server apacheds-protocol-dns diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 2a2f9084aeb4..01cb2747dbae 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -29,6 +29,21 @@ Netty/Testsuite/Autobahn + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + ${project.groupId} netty-codec-http diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 096235477747..b13a739ae665 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -29,6 +29,26 @@ Netty/Testsuite/Http2 + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + + + ${project.groupId} + netty-handler + ${project.version} + ${project.groupId} netty-codec-http diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 65bd3a483fb7..548e78b80f8d 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -29,6 +29,21 @@ Netty/Testsuite + + ${project.groupId} + netty-common + ${project.version} + + + ${project.groupId} + netty-buffer + ${project.version} + + + ${project.groupId} + netty-transport + ${project.version} + ${project.groupId} netty-transport-sctp diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index f804b7b02fce..3033703c0623 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -319,12 +319,12 @@ io.netty - netty-transport-native-unix-common + netty-transport ${project.version} io.netty - netty-transport + netty-transport-native-unix-common ${project.version} diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 50188e50efc2..8e8c2c5b77b6 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -379,12 +379,12 @@ io.netty - netty-transport-native-unix-common + netty-transport ${project.version} io.netty - netty-transport + netty-transport-native-unix-common ${project.version} diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index f366cc1ca6b2..0759ac556d5d 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -30,6 +30,11 @@ + + io.netty + netty-transport + ${project.version} + io.netty netty-transport-native-unix-common diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index b7f764123d2c..ffa4ba90e1f6 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -332,6 +332,11 @@ netty-common ${project.version} + + io.netty + netty-buffer + ${project.version} + io.netty netty-transport diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index ef29309f3ade..94a04c7dc945 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -33,6 +33,11 @@ + + ${project.groupId} + netty-common + ${project.version} + ${project.groupId} netty-buffer diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 38cf1ae2d5e1..07b03388acdc 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -37,12 +37,16 @@ + + ${project.groupId} + netty-common + ${project.version} + ${project.groupId} netty-buffer ${project.version} - ${project.groupId} netty-transport @@ -78,7 +82,7 @@ org.apache.maven.plugins maven-surefire-plugin - always + false diff --git a/transport/pom.xml b/transport/pom.xml index 10917e3756d1..6075b13505f4 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -33,6 +33,11 @@ + + ${project.groupId} + netty-common + ${project.version} + ${project.groupId} netty-buffer From 51a650979fe2a3ecb15fbd81141eff831cb39b7b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 6 Dec 2018 20:43:40 +0100 Subject: [PATCH 303/417] Skip test on windows as the semantics we expect are only true on Linux / Unix / BSD / MacOS (#8629) Motivation: In the test we assume some semantics on how RST is done that are not true for Windows so we should skip it. Modifications: Skip test when on windows. Result: Be able to run testsuite on windows. Fixes https://github.com/netty/netty/issues/8571. --- .../testsuite/transport/socket/SocketHalfClosedTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java index cd03ac3c9be7..2680810a9172 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketHalfClosedTest.java @@ -34,6 +34,8 @@ import io.netty.channel.socket.ChannelOutputShutdownEvent; import io.netty.channel.socket.DuplexChannel; import io.netty.util.UncheckedBooleanSupplier; +import io.netty.util.internal.PlatformDependent; +import org.junit.Assume; import org.junit.Test; import java.util.concurrent.CountDownLatch; @@ -229,6 +231,8 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { @Test public void testAutoCloseFalseDoesShutdownOutput() throws Throwable { + // This test only works on Linux / BSD / MacOS as we assume some semantics that are not true for Windows. + Assume.assumeFalse(PlatformDependent.isWindows()); run(); } From 2b651eb1a255cff4fb62eaf3e579d24b97e7fb2e Mon Sep 17 00:00:00 2001 From: tomer doron Date: Thu, 6 Dec 2018 20:43:06 -0800 Subject: [PATCH 304/417] support publishing snapshots from docker based ci (#8634) motivation: automate snapshot publishing from docker based ci changes: * add local settings.xml with env variables for publishing to sonatype-nexus-snapshots * pipe UID/PWD env variable in docker compose --- .mvn/settings.xml | 9 +++++++++ docker/docker-compose.yaml | 3 +++ 2 files changed, 12 insertions(+) create mode 100644 .mvn/settings.xml diff --git a/.mvn/settings.xml b/.mvn/settings.xml new file mode 100644 index 000000000000..1f7f6fafec76 --- /dev/null +++ b/.mvn/settings.xml @@ -0,0 +1,9 @@ + + + + sonatype-nexus-snapshots + ${env.SANOTYPE_USER} + ${env.SANOTYPE_PASSWORD} + + + diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index f0183cd283b3..3e3bd2cf6e9f 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -31,6 +31,9 @@ services: shell: <<: *common + environment: + - SANOTYPE_USER + - SANOTYPE_PASSWORD volumes: - ~/.ssh:/root/.ssh - ~/.gnupg:/root/.gnupg From 22b2c4c3b8be75ad3ea97f115d1bc56fa46069d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=9A=E5=B7=B4=E8=83=BA?= <1330456228zjp@gmail.com> Date: Fri, 7 Dec 2018 20:50:00 +0800 Subject: [PATCH 305/417] Fix concurrency problem in UniqueIpFilter (#8635) Motivation: If two requests from the same IP are reached at the same time, `connected.contains(remoteIp)` may return false in both threads. Modifications: Check if there is already a connection with the same IP using return values. Result: Become thread safe. --- .../handler/ipfilter/UniqueIpFilter.java | 3 +- .../handler/ipfilter/UniqueIpFilterTest.java | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 handler/src/test/java/io/netty/handler/ipfilter/UniqueIpFilterTest.java diff --git a/handler/src/main/java/io/netty/handler/ipfilter/UniqueIpFilter.java b/handler/src/main/java/io/netty/handler/ipfilter/UniqueIpFilter.java index ac346481d734..15099bb2a314 100644 --- a/handler/src/main/java/io/netty/handler/ipfilter/UniqueIpFilter.java +++ b/handler/src/main/java/io/netty/handler/ipfilter/UniqueIpFilter.java @@ -38,10 +38,9 @@ public class UniqueIpFilter extends AbstractRemoteAddressFilter future1 = newChannelAsync(barrier, executorService, ipFilter); + Future future2 = newChannelAsync(barrier, executorService, ipFilter); + EmbeddedChannel ch1 = future1.get(); + EmbeddedChannel ch2 = future2.get(); + Assert.assertTrue(ch1.isActive() || ch2.isActive()); + Assert.assertFalse(ch1.isActive() && ch2.isActive()); + + barrier.reset(); + ch1.close().await(); + ch2.close().await(); + } + } finally { + executorService.shutdown(); + } + } + + private static Future newChannelAsync(final CyclicBarrier barrier, + ExecutorService executorService, + final ChannelHandler... handler) { + return executorService.submit(new Callable() { + @Override + public EmbeddedChannel call() throws Exception { + barrier.await(); + return new EmbeddedChannel(handler) { + @Override + protected SocketAddress remoteAddress0() { + return isActive() ? SocketUtils.socketAddress("91.92.93.1", 5421) : null; + } + }; + } + }); + } + +} From a564b70d51d7e917d7d1af225fc69a535b3ceaa6 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 7 Dec 2018 19:12:06 +0100 Subject: [PATCH 306/417] More correct fix for using ChannelInitializer with custom EventExecutor. (#8633) Motivation: 8331248671b9c0ea07cf8dbdfa5d8d2f89fdf459 did make some changes to fix a race in ChannelInitializer when using with a custom EventExecutor. Unfortunally these where a bit racy and so the testcase failed sometimes. Modifications: - More correct fix when using a custom EventExecutor - Adjust the testcase to be more correct. Result: Proper fix for https://github.com/netty/netty/issues/8616. --- .../io/netty/channel/ChannelInitializer.java | 46 ++++++++++--------- .../netty/channel/ChannelInitializerTest.java | 22 ++++++--- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/ChannelInitializer.java b/transport/src/main/java/io/netty/channel/ChannelInitializer.java index 18344d200fa5..9aa4eaa57010 100644 --- a/transport/src/main/java/io/netty/channel/ChannelInitializer.java +++ b/transport/src/main/java/io/netty/channel/ChannelInitializer.java @@ -79,6 +79,9 @@ public final void channelRegistered(ChannelHandlerContext ctx) throws Exception // we called initChannel(...) so we need to call now pipeline.fireChannelRegistered() to ensure we not // miss an event. ctx.pipeline().fireChannelRegistered(); + + // We are done with init the Channel, removing all the state for the Channel now. + removeState(ctx); } else { // Called initChannel(...) before which is the expected behavior, so just forward the event. ctx.fireChannelRegistered(); @@ -106,7 +109,11 @@ public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering // surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers // will be added in the expected order. - initChannel(ctx); + if (initChannel(ctx)) { + + // We are done with init the Channel, removing the initializer now. + removeState(ctx); + } } } @@ -125,32 +132,29 @@ private boolean initChannel(ChannelHandlerContext ctx) throws Exception { // We do so to prevent multiple calls to initChannel(...). exceptionCaught(ctx, cause); } finally { - remove(ctx); + ChannelPipeline pipeline = ctx.pipeline(); + if (pipeline.context(this) != null) { + pipeline.remove(this); + } } return true; } return false; } - private void remove(final ChannelHandlerContext ctx) { - try { - ChannelPipeline pipeline = ctx.pipeline(); - if (pipeline.context(this) != null) { - pipeline.remove(this); - } - } finally { - // The removal may happen in an async fashion if the EventExecutor we use does something funky. - if (ctx.isRemoved()) { - initMap.remove(ctx); - } else { - // Ensure we always remove from the Map in all cases to not produce a memory leak. - ctx.channel().closeFuture().addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) { - initMap.remove(ctx); - } - }); - } + private void removeState(final ChannelHandlerContext ctx) { + // The removal may happen in an async fashion if the EventExecutor we use does something funky. + if (ctx.isRemoved()) { + initMap.remove(ctx); + } else { + // The context is not removed yet which is most likely the case because a custom EventExecutor is used. + // Let's schedule it on the EventExecutor to give it some more time to be completed in case it is offloaded. + ctx.executor().execute(new Runnable() { + @Override + public void run() { + initMap.remove(ctx); + } + }); } } } diff --git a/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java b/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java index 2ac1bcdefa9f..bebf2d5e58d3 100644 --- a/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java +++ b/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java @@ -29,6 +29,7 @@ import java.util.Iterator; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -312,6 +313,7 @@ public void execute(Runnable command) { } }; + final CountDownLatch latch = new CountDownLatch(1); ServerBootstrap serverBootstrap = new ServerBootstrap() .channel(LocalServerChannel.class) .group(group) @@ -331,13 +333,20 @@ protected void initChannel(Channel ch) { public void channelRead(ChannelHandlerContext ctx, Object msg) { // just drop on the floor. } + + @Override + public void channelUnregistered(ChannelHandlerContext ctx) { + latch.countDown(); + } }); completeCount.incrementAndGet(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - errorRef.set(cause); + if (cause instanceof AssertionError) { + errorRef.set(cause); + } } }); } @@ -360,19 +369,18 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { client.closeFuture().sync(); server.closeFuture().sync(); - // Give some time to execute everything that was submitted before. - Thread.sleep(1000); + latch.await(); - executor.shutdown(); - assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS)); - - assertEquals(invokeCount.get(), 1); + assertEquals(1, invokeCount.get()); assertEquals(invokeCount.get(), completeCount.get()); Throwable cause = errorRef.get(); if (cause != null) { throw cause; } + + executor.shutdown(); + assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS)); } private static void closeChannel(Channel c) { From 36c12a4c55a4d19f830ca0df2e48fa43b052a2fa Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 7 Dec 2018 20:45:26 +0100 Subject: [PATCH 307/417] Fix typo in MessageToMessageDecoder api docs. (#8638) Motivation: We had some typo (most likely caused by copy-and-paste) in the api docs which should be fixed. Modifications: Replace encoder by decoder word. Result: Correct apidocs. --- .../java/io/netty/handler/codec/MessageToMessageDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java index 0337e768a0c7..ec828e2e3281 100644 --- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java @@ -107,7 +107,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception /** * Decode from one message to an other. This method will be called for each written message that can be handled - * by this encoder. + * by this decoder. * * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageDecoder} belongs to * @param msg the message to decode to an other one From bdcad8ef47b349500755ccd7bce34cc20b8281a2 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 7 Dec 2018 21:00:47 +0100 Subject: [PATCH 308/417] Fix incorrect assert in Http2MultiplexCodec caused by 9f9aa1a. (#8639) Motivation: 9f9aa1a did some changes related to fixing how we handle ctx.read() in child channel but did incorrectly change some assert. Modifications: Fix assert to be correct. Result: Code does not throw an AssertionError due incorrect assert check. --- .../java/io/netty/handler/codec/http2/Http2MultiplexCodec.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java index 6de1dd2df910..46aa97a5360e 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java @@ -805,7 +805,7 @@ void fireChildRead(Http2Frame frame) { void fireChildReadComplete() { assert eventLoop().inEventLoop(); - assert readStatus == ReadStatus.IN_PROGRESS; + assert readStatus != ReadStatus.IDLE; unsafe.notifyReadComplete(unsafe.recvBufAllocHandle()); } From 25216be1182b042a07ab08751b020e42d2f29b89 Mon Sep 17 00:00:00 2001 From: Paul Verest Date: Tue, 11 Dec 2018 03:50:38 +0800 Subject: [PATCH 309/417] ReadTimeoutHandler - missing ) within JavaDoc example (#8645) Motivation: improve docs Modification: ReadTimeoutHandler - missing ) within JavaDoc example No logic/unit tests affected --- .../main/java/io/netty/handler/timeout/ReadTimeoutHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java b/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java index 1fe47889d8a4..cba6e60d720e 100644 --- a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java @@ -33,7 +33,7 @@ * * public class MyChannelInitializer extends {@link ChannelInitializer}<{@link Channel}> { * public void initChannel({@link Channel} channel) { - * channel.pipeline().addLast("readTimeoutHandler", new {@link ReadTimeoutHandler}(30); + * channel.pipeline().addLast("readTimeoutHandler", new {@link ReadTimeoutHandler}(30)); * channel.pipeline().addLast("myHandler", new MyHandler()); * } * } From 1dacd3798939a35ed2105094ca24a387812b68dd Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 12 Dec 2018 07:41:26 +0100 Subject: [PATCH 310/417] SSLSession.putValue / getValue / removeValue / getValueNames must be thread-safe. (#8648) Motivation: SSLSession.putValue / getValue / removeValue / getValueNames must be thread-safe as it may be called from multiple threads. This is also the case in the OpenJDK implementation. Modifications: Guard with synchronized (this) blocks to keep the memory overhead low as we do not expect to have these called frequently. Result: SSLSession implementation is thread-safe. --- .../ssl/ReferenceCountedOpenSslEngine.java | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 4537ee75f89a..7ec35b04bc1e 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -2016,12 +2016,16 @@ public void putValue(String name, Object value) { if (value == null) { throw new NullPointerException("value"); } - Map values = this.values; - if (values == null) { - // Use size of 2 to keep the memory overhead small - values = this.values = new HashMap(2); + final Object old; + synchronized (this) { + Map values = this.values; + if (values == null) { + // Use size of 2 to keep the memory overhead small + values = this.values = new HashMap(2); + } + old = values.put(name, value); } - Object old = values.put(name, value); + if (value instanceof SSLSessionBindingListener) { // Use newSSLSessionBindingEvent so we alway use the wrapper if needed. ((SSLSessionBindingListener) value).valueBound(newSSLSessionBindingEvent(name)); @@ -2034,10 +2038,12 @@ public Object getValue(String name) { if (name == null) { throw new NullPointerException("name"); } - if (values == null) { - return null; + synchronized (this) { + if (values == null) { + return null; + } + return values.get(name); } - return values.get(name); } @Override @@ -2045,21 +2051,28 @@ public void removeValue(String name) { if (name == null) { throw new NullPointerException("name"); } - Map values = this.values; - if (values == null) { - return; + + final Object old; + synchronized (this) { + Map values = this.values; + if (values == null) { + return; + } + old = values.remove(name); } - Object old = values.remove(name); + notifyUnbound(old, name); } @Override public String[] getValueNames() { - Map values = this.values; - if (values == null || values.isEmpty()) { - return EmptyArrays.EMPTY_STRINGS; + synchronized (this) { + Map values = this.values; + if (values == null || values.isEmpty()) { + return EmptyArrays.EMPTY_STRINGS; + } + return values.keySet().toArray(new String[0]); } - return values.keySet().toArray(new String[0]); } private void notifyUnbound(Object value, String name) { From d17bd5e16071b70787399118acc0aa0ea3da7ac2 Mon Sep 17 00:00:00 2001 From: Feri73 Date: Wed, 12 Dec 2018 01:29:02 -0800 Subject: [PATCH 311/417] Adding support for whitespace in resource path in tests (#8606) Motivation: In windows if the project is in a path that contains whitespace, resources cannot be accessed and tests fail. Modifications: Adds ResourcesUtil.java in netty-common. Tests use ResourcesUtil.java to access a resource. Result: Being able to build netty in a path containing whitespace --- .../netty/handler/codec/http2/HpackTest.java | 5 +- .../io/netty/util/internal/ResourcesUtil.java | 43 ++++++++++++++++ .../ssl/ParameterizedSslHandlerTest.java | 6 +-- .../io/netty/handler/ssl/SSLEngineTest.java | 51 ++++++++++--------- .../io/netty/handler/ssl/SniHandlerTest.java | 7 +-- .../io/netty/handler/ssl/SslContextTest.java | 29 ++++++----- .../netty/resolver/HostsFileParserTest.java | 6 +-- 7 files changed, 96 insertions(+), 51 deletions(-) create mode 100644 common/src/main/java/io/netty/util/internal/ResourcesUtil.java diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackTest.java index 77da8665c0a5..fe9fa3260138 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackTest.java @@ -31,6 +31,7 @@ */ package io.netty.handler.codec.http2; +import io.netty.util.internal.ResourcesUtil; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -38,7 +39,6 @@ import java.io.File; import java.io.InputStream; -import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -56,8 +56,7 @@ public HpackTest(String fileName) { @Parameters(name = "{0}") public static Collection data() { - URL url = HpackTest.class.getResource(TEST_DIR); - File[] files = new File(url.getFile()).listFiles(); + File[] files = ResourcesUtil.getFile(HpackTest.class, TEST_DIR).listFiles(); if (files == null) { throw new NullPointerException("files"); } diff --git a/common/src/main/java/io/netty/util/internal/ResourcesUtil.java b/common/src/main/java/io/netty/util/internal/ResourcesUtil.java new file mode 100644 index 000000000000..7b1c8ea695d4 --- /dev/null +++ b/common/src/main/java/io/netty/util/internal/ResourcesUtil.java @@ -0,0 +1,43 @@ +/* + * Copyright 2018 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: + * + * http://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.internal; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +/** + * A utility class that provides various common operations and constants + * related to loading resources + */ +public final class ResourcesUtil { + + /** + * Returns a {@link File} named {@code fileName} associated with {@link Class} {@code resourceClass} . + * + * @param resourceClass The associated class + * @param fileName The file name + * @return The file named {@code fileName} associated with {@link Class} {@code resourceClass} . + */ + public static File getFile(Class resourceClass, String fileName) { + try { + return new File(URLDecoder.decode(resourceClass.getResource(fileName).getFile(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + return new File(resourceClass.getResource(fileName).getFile()); + } + } + + private ResourcesUtil() { } +} diff --git a/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java index 780ddf670eec..6d01a2673e77 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java @@ -33,6 +33,7 @@ import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SimpleTrustManagerFactory; import io.netty.util.ReferenceCountUtil; +import io.netty.util.internal.ResourcesUtil; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.Promise; @@ -46,7 +47,6 @@ import javax.net.ssl.SSLException; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import java.io.File; import java.net.InetSocketAddress; import java.security.KeyStore; import java.security.cert.CertificateException; @@ -302,8 +302,8 @@ public X509Certificate[] getAcceptedIssuers() { final SslContext sslClientCtx = SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) - .keyManager(new File(getClass().getResource("test.crt").getFile()), - new File(getClass().getResource("test_unencrypted.pem").getFile())) + .keyManager(ResourcesUtil.getFile(getClass(), "test.crt"), + ResourcesUtil.getFile(getClass(), "test_unencrypted.pem")) .sslProvider(clientProvider).build(); NioEventLoopGroup group = new NioEventLoopGroup(); diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index c23800162ad5..a006fad7207f 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -39,6 +39,7 @@ import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; import io.netty.util.ReferenceCountUtil; +import io.netty.util.internal.ResourcesUtil; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; import io.netty.util.internal.EmptyArrays; @@ -501,9 +502,9 @@ public void tearDown() throws InterruptedException { @Test public void testMutualAuthSameCerts() throws Throwable { - mySetupMutualAuth(new File(getClass().getResource("test_unencrypted.pem").getFile()), - new File(getClass().getResource("test.crt").getFile()), - null); + mySetupMutualAuth(ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"), + ResourcesUtil.getFile(getClass(), "test.crt"), + null); runTest(null); assertTrue(serverLatch.await(2, TimeUnit.SECONDS)); Throwable cause = serverException; @@ -514,11 +515,11 @@ public void testMutualAuthSameCerts() throws Throwable { @Test public void testMutualAuthDiffCerts() throws Exception { - File serverKeyFile = new File(getClass().getResource("test_encrypted.pem").getFile()); - File serverCrtFile = new File(getClass().getResource("test.crt").getFile()); + File serverKeyFile = ResourcesUtil.getFile(getClass(), "test_encrypted.pem"); + File serverCrtFile = ResourcesUtil.getFile(getClass(), "test.crt"); String serverKeyPassword = "12345"; - File clientKeyFile = new File(getClass().getResource("test2_encrypted.pem").getFile()); - File clientCrtFile = new File(getClass().getResource("test2.crt").getFile()); + File clientKeyFile = ResourcesUtil.getFile(getClass(), "test2_encrypted.pem"); + File clientCrtFile = ResourcesUtil.getFile(getClass(), "test2.crt"); String clientKeyPassword = "12345"; mySetupMutualAuth(clientCrtFile, serverKeyFile, serverCrtFile, serverKeyPassword, serverCrtFile, clientKeyFile, clientCrtFile, clientKeyPassword); @@ -528,11 +529,11 @@ public void testMutualAuthDiffCerts() throws Exception { @Test public void testMutualAuthDiffCertsServerFailure() throws Exception { - File serverKeyFile = new File(getClass().getResource("test_encrypted.pem").getFile()); - File serverCrtFile = new File(getClass().getResource("test.crt").getFile()); + File serverKeyFile = ResourcesUtil.getFile(getClass(), "test_encrypted.pem"); + File serverCrtFile = ResourcesUtil.getFile(getClass(), "test.crt"); String serverKeyPassword = "12345"; - File clientKeyFile = new File(getClass().getResource("test2_encrypted.pem").getFile()); - File clientCrtFile = new File(getClass().getResource("test2.crt").getFile()); + File clientKeyFile = ResourcesUtil.getFile(getClass(), "test2_encrypted.pem"); + File clientCrtFile = ResourcesUtil.getFile(getClass(), "test2.crt"); String clientKeyPassword = "12345"; // Client trusts server but server only trusts itself mySetupMutualAuth(serverCrtFile, serverKeyFile, serverCrtFile, serverKeyPassword, @@ -543,11 +544,11 @@ public void testMutualAuthDiffCertsServerFailure() throws Exception { @Test public void testMutualAuthDiffCertsClientFailure() throws Exception { - File serverKeyFile = new File(getClass().getResource("test_unencrypted.pem").getFile()); - File serverCrtFile = new File(getClass().getResource("test.crt").getFile()); + File serverKeyFile = ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"); + File serverCrtFile = ResourcesUtil.getFile(getClass(), "test.crt"); String serverKeyPassword = null; - File clientKeyFile = new File(getClass().getResource("test2_unencrypted.pem").getFile()); - File clientCrtFile = new File(getClass().getResource("test2.crt").getFile()); + File clientKeyFile = ResourcesUtil.getFile(getClass(), "test2_unencrypted.pem"); + File clientCrtFile = ResourcesUtil.getFile(getClass(), "test2.crt"); String clientKeyPassword = null; // Server trusts client but client only trusts itself mySetupMutualAuth(clientCrtFile, serverKeyFile, serverCrtFile, serverKeyPassword, @@ -593,7 +594,7 @@ private void testMutualAuthInvalidClientCertSucceed(ClientAuth auth) throws Exce final KeyManagerFactory clientKeyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); clientKeyManagerFactory.init(clientKeyStore, password); - File commonCertChain = new File(getClass().getResource("mutual_auth_ca.pem").getFile()); + File commonCertChain = ResourcesUtil.getFile(getClass(), "mutual_auth_ca.pem"); mySetupMutualAuth(serverKeyManagerFactory, commonCertChain, clientKeyManagerFactory, commonCertChain, auth, false, false); @@ -620,7 +621,7 @@ private void testMutualAuthClientCertFail(ClientAuth auth, String clientCert, bo final KeyManagerFactory clientKeyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); clientKeyManagerFactory.init(clientKeyStore, password); - File commonCertChain = new File(getClass().getResource("mutual_auth_ca.pem").getFile()); + File commonCertChain = ResourcesUtil.getFile(getClass(), "mutual_auth_ca.pem"); mySetupMutualAuth(serverKeyManagerFactory, commonCertChain, clientKeyManagerFactory, commonCertChain, auth, true, serverInitEngine); @@ -784,9 +785,9 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E @Test public void testClientHostnameValidationSuccess() throws InterruptedException, SSLException { - mySetupClientHostnameValidation(new File(getClass().getResource("localhost_server.pem").getFile()), - new File(getClass().getResource("localhost_server.key").getFile()), - new File(getClass().getResource("mutual_auth_ca.pem").getFile()), + mySetupClientHostnameValidation(ResourcesUtil.getFile(getClass(), "localhost_server.pem"), + ResourcesUtil.getFile(getClass(), "localhost_server.key"), + ResourcesUtil.getFile(getClass(), "mutual_auth_ca.pem"), false); assertTrue(clientLatch.await(5, TimeUnit.SECONDS)); assertNull(clientException); @@ -796,9 +797,9 @@ public void testClientHostnameValidationSuccess() throws InterruptedException, S @Test public void testClientHostnameValidationFail() throws InterruptedException, SSLException { - mySetupClientHostnameValidation(new File(getClass().getResource("notlocalhost_server.pem").getFile()), - new File(getClass().getResource("notlocalhost_server.key").getFile()), - new File(getClass().getResource("mutual_auth_ca.pem").getFile()), + mySetupClientHostnameValidation(ResourcesUtil.getFile(getClass(), "notlocalhost_server.pem"), + ResourcesUtil.getFile(getClass(), "notlocalhost_server.key"), + ResourcesUtil.getFile(getClass(), "mutual_auth_ca.pem"), true); assertTrue(clientLatch.await(5, TimeUnit.SECONDS)); assertTrue("unexpected exception: " + clientException, @@ -1347,8 +1348,8 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { protected void testEnablingAnAlreadyDisabledSslProtocol(String[] protocols1, String[] protocols2) throws Exception { SSLEngine sslEngine = null; try { - File serverKeyFile = new File(getClass().getResource("test_unencrypted.pem").getFile()); - File serverCrtFile = new File(getClass().getResource("test.crt").getFile()); + File serverKeyFile = ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"); + File serverCrtFile = ResourcesUtil.getFile(getClass(), "test.crt"); serverSslCtx = SslContextBuilder.forServer(serverCrtFile, serverKeyFile) .sslProvider(sslServerProvider()) .sslContextProvider(serverSslContextProvider()) diff --git a/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java index a6dceb364e70..d3bf1f24285a 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java @@ -44,6 +44,7 @@ import io.netty.util.Mapping; import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCounted; +import io.netty.util.internal.ResourcesUtil; import io.netty.util.concurrent.Promise; import io.netty.util.internal.ObjectUtil; import io.netty.util.internal.StringUtil; @@ -98,8 +99,8 @@ private static SslContext makeSslContext(SslProvider provider, boolean apn) thro assumeApnSupported(provider); } - File keyFile = new File(SniHandlerTest.class.getResource("test_encrypted.pem").getFile()); - File crtFile = new File(SniHandlerTest.class.getResource("test.crt").getFile()); + File keyFile = ResourcesUtil.getFile(SniHandlerTest.class, "test_encrypted.pem"); + File crtFile = ResourcesUtil.getFile(SniHandlerTest.class, "test.crt"); SslContextBuilder sslCtxBuilder = SslContextBuilder.forServer(crtFile, keyFile, "12345") .sslProvider(provider); @@ -114,7 +115,7 @@ private static SslContext makeSslClientContext(SslProvider provider, boolean apn assumeApnSupported(provider); } - File crtFile = new File(SniHandlerTest.class.getResource("test.crt").getFile()); + File crtFile = ResourcesUtil.getFile(SniHandlerTest.class, "test.crt"); SslContextBuilder sslCtxBuilder = SslContextBuilder.forClient().trustManager(crtFile).sslProvider(provider); if (apn) { diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java index eb845d5651c0..247575b2dfa7 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java @@ -15,6 +15,7 @@ */ package io.netty.handler.ssl; +import io.netty.util.internal.ResourcesUtil; import org.junit.Assert; import org.junit.Test; @@ -38,58 +39,58 @@ public abstract class SslContextTest { @Test(expected = IOException.class) public void testUnencryptedEmptyPassword() throws Exception { PrivateKey key = SslContext.toPrivateKey( - new File(getClass().getResource("test2_unencrypted.pem").getFile()), ""); + ResourcesUtil.getFile(getClass(), "test2_unencrypted.pem"), ""); Assert.assertNotNull(key); } @Test public void testUnEncryptedNullPassword() throws Exception { PrivateKey key = SslContext.toPrivateKey( - new File(getClass().getResource("test2_unencrypted.pem").getFile()), null); + ResourcesUtil.getFile(getClass(), "test2_unencrypted.pem"), null); Assert.assertNotNull(key); } @Test public void testEncryptedEmptyPassword() throws Exception { PrivateKey key = SslContext.toPrivateKey( - new File(getClass().getResource("test_encrypted_empty_pass.pem").getFile()), ""); + ResourcesUtil.getFile(getClass(), "test_encrypted_empty_pass.pem"), ""); Assert.assertNotNull(key); } @Test(expected = InvalidKeySpecException.class) public void testEncryptedNullPassword() throws Exception { SslContext.toPrivateKey( - new File(getClass().getResource("test_encrypted_empty_pass.pem").getFile()), null); + ResourcesUtil.getFile(getClass(), "test_encrypted_empty_pass.pem"), null); } @Test public void testSslServerWithEncryptedPrivateKey() throws SSLException { - File keyFile = new File(getClass().getResource("test_encrypted.pem").getFile()); - File crtFile = new File(getClass().getResource("test.crt").getFile()); + File keyFile = ResourcesUtil.getFile(getClass(), "test_encrypted.pem"); + File crtFile = ResourcesUtil.getFile(getClass(), "test.crt"); newServerContext(crtFile, keyFile, "12345"); } @Test public void testSslServerWithEncryptedPrivateKey2() throws SSLException { - File keyFile = new File(getClass().getResource("test2_encrypted.pem").getFile()); - File crtFile = new File(getClass().getResource("test2.crt").getFile()); + File keyFile = ResourcesUtil.getFile(getClass(), "test2_encrypted.pem"); + File crtFile = ResourcesUtil.getFile(getClass(), "test2.crt"); newServerContext(crtFile, keyFile, "12345"); } @Test public void testSslServerWithUnencryptedPrivateKey() throws SSLException { - File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile()); - File crtFile = new File(getClass().getResource("test.crt").getFile()); + File keyFile = ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"); + File crtFile = ResourcesUtil.getFile(getClass(), "test.crt"); newServerContext(crtFile, keyFile, null); } @Test(expected = SSLException.class) public void testSslServerWithUnencryptedPrivateKeyEmptyPass() throws SSLException { - File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile()); - File crtFile = new File(getClass().getResource("test.crt").getFile()); + File keyFile = ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"); + File crtFile = ResourcesUtil.getFile(getClass(), "test.crt"); newServerContext(crtFile, keyFile, ""); } @@ -108,8 +109,8 @@ public void testSupportedCiphers() throws KeyManagementException, NoSuchAlgorith exception = e; } assumeNotNull(exception); - File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile()); - File crtFile = new File(getClass().getResource("test.crt").getFile()); + File keyFile = ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"); + File crtFile = ResourcesUtil.getFile(getClass(), "test.crt"); SslContext sslContext = newServerContext(crtFile, keyFile, null); assertFalse(sslContext.cipherSuites().contains(unsupportedCipher)); diff --git a/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java b/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java index ac4e4b2eb695..ee07c592cae4 100644 --- a/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java +++ b/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java @@ -16,11 +16,11 @@ package io.netty.resolver; import io.netty.util.CharsetUtil; +import io.netty.util.internal.ResourcesUtil; import org.junit.Assume; import org.junit.Test; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.StringReader; import java.net.Inet4Address; @@ -76,7 +76,7 @@ public void testParseUnicode() throws IOException { return; } testParseFile(HostsFileParser.parse( - new File(getClass().getResource("hosts-unicode").getFile()), unicodeCharset)); + ResourcesUtil.getFile(getClass(), "hosts-unicode"), unicodeCharset)); } @Test @@ -88,7 +88,7 @@ public void testParseMultipleCharsets() throws IOException { Assume.assumeNoException(e); return; } - testParseFile(HostsFileParser.parse(new File(getClass().getResource("hosts-unicode").getFile()), + testParseFile(HostsFileParser.parse(ResourcesUtil.getFile(getClass(), "hosts-unicode"), CharsetUtil.UTF_8, CharsetUtil.ISO_8859_1, unicodeCharset)); } From 83ab4ef5e372e0a9b87fdadd4077a551af5b2e64 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 13 Dec 2018 19:02:20 +0100 Subject: [PATCH 312/417] Explict always call ctx.read() when AUTO_READ is false and HTTP/2 is used. (#8647) Motivation: We should always call ctx.read() even when AUTO_READ is false as flow-control is enforced by the HTTP/2 protocol. See also https://tools.ietf.org/html/rfc7540#section-5.2.2. We already did this before but not explicit and only did so because of some implementation details of ByteToMessageDecoder. It's better to be explicit here to not risk of breakage later on. Modifications: - Ensure we always call ctx.read() when AUTO_READ is false - Add unit test. Result: No risk of staling the connection when HTTP/2 is used. --- .../codec/http2/Http2ConnectionHandler.java | 14 ++++++++++++-- .../codec/http2/Http2ConnectionHandlerTest.java | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java index 3151f1ccb2f1..4f66e918d54e 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java @@ -527,8 +527,18 @@ public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } } - void channelReadComplete0(ChannelHandlerContext ctx) throws Exception { - super.channelReadComplete(ctx); + final void channelReadComplete0(ChannelHandlerContext ctx) { + // Discard bytes of the cumulation buffer if needed. + discardSomeReadBytes(); + + // Ensure we never stale the HTTP/2 Channel. Flow-control is enforced by HTTP/2. + // + // See https://tools.ietf.org/html/rfc7540#section-5.2.2 + if (!ctx.channel().config().isAutoRead()) { + ctx.read(); + } + + ctx.fireChannelReadComplete(); } /** diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java index 223d6005f495..9b8c62402723 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java @@ -22,8 +22,10 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; +import io.netty.channel.DefaultChannelConfig; import io.netty.channel.DefaultChannelPromise; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator; @@ -142,6 +144,11 @@ public void setup() throws Exception { promise = new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE); voidPromise = new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE); + + when(channel.metadata()).thenReturn(new ChannelMetadata(false)); + DefaultChannelConfig config = new DefaultChannelConfig(channel); + when(channel.config()).thenReturn(config); + Throwable fakeException = new RuntimeException("Fake exception"); when(encoder.connection()).thenReturn(connection); when(decoder.connection()).thenReturn(connection); @@ -684,6 +691,14 @@ public void channelReadCompleteTriggersFlush() throws Exception { verify(ctx, times(1)).flush(); } + @Test + public void channelReadCompleteCallsReadWhenAutoReadFalse() throws Exception { + channel.config().setAutoRead(false); + handler = newHandler(); + handler.channelReadComplete(ctx); + verify(ctx, times(1)).read(); + } + @Test public void channelClosedDoesNotThrowPrefaceException() throws Exception { when(connection.isServer()).thenReturn(true); From db6d94f82a0f860131392aa483f4d1e21dc74249 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Fri, 14 Dec 2018 10:27:37 +0100 Subject: [PATCH 313/417] Support 1012, 1013 and 1014 WebSocket status code Motivation: RFC 6455 doesn't define status codes 1012, 1013 and 1014. Yet, since then, IANA has defined them, web browsers support them, applications in the wild do use them but it's currently not possible to buid a Netty based client for those services. From https://www.iana.org/assignments/websocket/websocket.xhtml: * 1012: Service Restart * 1013: Try Again Later * 1014: The server was acting as a gateway or proxy and received an invalid response from the upstream server. This is similar to 502 HTTP Status Code. Modification: Make status codes 1012, 1013 and 1014 legit. Result: WebSocket status codes as defined by IANA are supported. --- .../websocketx/WebSocket08FrameDecoder.java | 2 +- .../WebSocket08FrameDecoderTest.java | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java index 604746c5ae2f..f5a6dc193600 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java @@ -451,7 +451,7 @@ protected void checkCloseFrameBody( // Must have 2 byte integer within the valid range int statusCode = buffer.readShort(); if (statusCode >= 0 && statusCode <= 999 || statusCode >= 1004 && statusCode <= 1006 - || statusCode >= 1012 && statusCode <= 2999) { + || statusCode >= 1015 && statusCode <= 2999) { protocolViolation(ctx, "Invalid close frame getStatus code: " + statusCode); } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java index 547eed6174e8..029f7db447cc 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java @@ -12,11 +12,20 @@ */ package io.netty.handler.codec.http.websocketx; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import org.junit.Test; +import static org.junit.Assert.*; import org.mockito.Mockito; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + public class WebSocket08FrameDecoderTest { @Test @@ -26,4 +35,50 @@ public void channelInactive() throws Exception { decoder.channelInactive(ctx); Mockito.verify(ctx).fireChannelInactive(); } + + @Test + public void supportIanaStatusCodes() throws Exception { + Set forbiddenIanaCodes = new HashSet(); + forbiddenIanaCodes.add(1004); + forbiddenIanaCodes.add(1005); + forbiddenIanaCodes.add(1006); + Set validIanaCodes = new HashSet(); + for (int i = 1000; i < 1015; i++) { + validIanaCodes.add(i); + } + validIanaCodes.removeAll(forbiddenIanaCodes); + + ChannelHandlerContext ctx = Mockito.mock(ChannelHandlerContext.class); + Mockito.when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); + + Channel channel = Mockito.mock(Channel.class); + Mockito.when(channel.isActive()).thenReturn(false); + Mockito.when(ctx.channel()).thenReturn(channel); + + List out = new ArrayList(); + + for (int statusCode: validIanaCodes) { + WebSocket08FrameEncoder encoder = new WebSocket08FrameEncoder(true); + WebSocket08FrameDecoder decoder = new WebSocket08FrameDecoder(true, true, 65535, false); + + CloseWebSocketFrame inputFrame = new CloseWebSocketFrame(statusCode, "Bye"); + try { + encoder.encode(ctx, inputFrame, out); + ByteBuf serializedCloseFrame = (ByteBuf) out.get(0); + out.clear(); + + decoder.decode(ctx, serializedCloseFrame, out); + CloseWebSocketFrame outputFrame = (CloseWebSocketFrame) out.get(0); + out.clear(); + + try { + assertEquals(statusCode, outputFrame.statusCode()); + } finally { + outputFrame.release(); + } + } finally { + inputFrame.release(); + } + } + } } From 29d185b7960863a8aec5347fc0d32016b3687403 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 14 Dec 2018 18:24:30 +0100 Subject: [PATCH 314/417] Revert "Support 1012, 1013 and 1014 WebSocket status code" This reverts commit db6d94f82a0f860131392aa483f4d1e21dc74249. --- .../websocketx/WebSocket08FrameDecoder.java | 2 +- .../WebSocket08FrameDecoderTest.java | 55 ------------------- 2 files changed, 1 insertion(+), 56 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java index f5a6dc193600..604746c5ae2f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java @@ -451,7 +451,7 @@ protected void checkCloseFrameBody( // Must have 2 byte integer within the valid range int statusCode = buffer.readShort(); if (statusCode >= 0 && statusCode <= 999 || statusCode >= 1004 && statusCode <= 1006 - || statusCode >= 1015 && statusCode <= 2999) { + || statusCode >= 1012 && statusCode <= 2999) { protocolViolation(ctx, "Invalid close frame getStatus code: " + statusCode); } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java index 029f7db447cc..547eed6174e8 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java @@ -12,20 +12,11 @@ */ package io.netty.handler.codec.http.websocketx; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import org.junit.Test; -import static org.junit.Assert.*; import org.mockito.Mockito; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - public class WebSocket08FrameDecoderTest { @Test @@ -35,50 +26,4 @@ public void channelInactive() throws Exception { decoder.channelInactive(ctx); Mockito.verify(ctx).fireChannelInactive(); } - - @Test - public void supportIanaStatusCodes() throws Exception { - Set forbiddenIanaCodes = new HashSet(); - forbiddenIanaCodes.add(1004); - forbiddenIanaCodes.add(1005); - forbiddenIanaCodes.add(1006); - Set validIanaCodes = new HashSet(); - for (int i = 1000; i < 1015; i++) { - validIanaCodes.add(i); - } - validIanaCodes.removeAll(forbiddenIanaCodes); - - ChannelHandlerContext ctx = Mockito.mock(ChannelHandlerContext.class); - Mockito.when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); - - Channel channel = Mockito.mock(Channel.class); - Mockito.when(channel.isActive()).thenReturn(false); - Mockito.when(ctx.channel()).thenReturn(channel); - - List out = new ArrayList(); - - for (int statusCode: validIanaCodes) { - WebSocket08FrameEncoder encoder = new WebSocket08FrameEncoder(true); - WebSocket08FrameDecoder decoder = new WebSocket08FrameDecoder(true, true, 65535, false); - - CloseWebSocketFrame inputFrame = new CloseWebSocketFrame(statusCode, "Bye"); - try { - encoder.encode(ctx, inputFrame, out); - ByteBuf serializedCloseFrame = (ByteBuf) out.get(0); - out.clear(); - - decoder.decode(ctx, serializedCloseFrame, out); - CloseWebSocketFrame outputFrame = (CloseWebSocketFrame) out.get(0); - out.clear(); - - try { - assertEquals(statusCode, outputFrame.statusCode()); - } finally { - outputFrame.release(); - } - } finally { - inputFrame.release(); - } - } - } } From a3844da10bb3ae788017b4c0699373284d60b058 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 14 Dec 2018 21:31:21 +0100 Subject: [PATCH 315/417] NoClassDefFoundError on Android platform when try to use DefaultDnsServerAddressStreamProvider. (#8656) Motivation: Andoid does not contain javax.naming.* so we should not try to use it to prevent a NoClassDefFoundError on init. Modifications: Only try to use javax.naming.* to retrieve nameservers when not using Android. Result: Fixes https://github.com/netty/netty/issues/8654. --- ...DefaultDnsServerAddressStreamProvider.java | 47 ++--------- .../netty/resolver/dns/DirContextUtils.java | 77 +++++++++++++++++++ 2 files changed, 82 insertions(+), 42 deletions(-) create mode 100644 resolver-dns/src/main/java/io/netty/resolver/dns/DirContextUtils.java diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java index a5ca38d368b1..00be07229520 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java @@ -16,23 +16,17 @@ package io.netty.resolver.dns; import io.netty.util.NetUtil; +import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.SocketUtils; import io.netty.util.internal.UnstableApi; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; import java.lang.reflect.Method; import java.net.Inet6Address; import java.net.InetSocketAddress; -import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; -import java.util.Hashtable; import java.util.List; import static io.netty.resolver.dns.DnsServerAddresses.sequential; @@ -55,41 +49,10 @@ public final class DefaultDnsServerAddressStreamProvider implements DnsServerAdd static { final List defaultNameServers = new ArrayList(2); - - // Using jndi-dns to obtain the default name servers. - // - // See: - // - http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html - // - http://mail.openjdk.java.net/pipermail/net-dev/2017-March/010695.html - Hashtable env = new Hashtable(); - env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory"); - env.put("java.naming.provider.url", "dns://"); - try { - DirContext ctx = new InitialDirContext(env); - String dnsUrls = (String) ctx.getEnvironment().get("java.naming.provider.url"); - // Only try if not empty as otherwise we will produce an exception - if (dnsUrls != null && !dnsUrls.isEmpty()) { - String[] servers = dnsUrls.split(" "); - for (String server : servers) { - try { - URI uri = new URI(server); - String host = new URI(server).getHost(); - - if (host == null || host.isEmpty()) { - logger.debug( - "Skipping a nameserver URI as host portion could not be extracted: {}", server); - // If the host portion can not be parsed we should just skip this entry. - continue; - } - int port = uri.getPort(); - defaultNameServers.add(SocketUtils.socketAddress(uri.getHost(), port == -1 ? DNS_PORT : port)); - } catch (URISyntaxException e) { - logger.debug("Skipping a malformed nameserver URI: {}", server, e); - } - } - } - } catch (NamingException ignore) { - // Will try reflection if this fails. + if (!PlatformDependent.isAndroid()) { + // Only try to use when not on Android as the classes not exists there: + // See https://github.com/netty/netty/issues/8654 + DirContextUtils.addNameServers(defaultNameServers, DNS_PORT); } if (defaultNameServers.isEmpty()) { diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DirContextUtils.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DirContextUtils.java new file mode 100644 index 000000000000..45c1ac433f2e --- /dev/null +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DirContextUtils.java @@ -0,0 +1,77 @@ +/* + * Copyright 2018 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: + * + * http://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.resolver.dns; + +import io.netty.util.internal.SocketUtils; +import io.netty.util.internal.logging.InternalLogger; +import io.netty.util.internal.logging.InternalLoggerFactory; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Hashtable; +import java.util.List; + +final class DirContextUtils { + private static final InternalLogger logger = + InternalLoggerFactory.getInstance(DirContextUtils.class); + + private DirContextUtils() { } + + static void addNameServers(List defaultNameServers, int defaultPort) { + // Using jndi-dns to obtain the default name servers. + // + // See: + // - http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html + // - http://mail.openjdk.java.net/pipermail/net-dev/2017-March/010695.html + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory"); + env.put("java.naming.provider.url", "dns://"); + + try { + DirContext ctx = new InitialDirContext(env); + String dnsUrls = (String) ctx.getEnvironment().get("java.naming.provider.url"); + // Only try if not empty as otherwise we will produce an exception + if (dnsUrls != null && !dnsUrls.isEmpty()) { + String[] servers = dnsUrls.split(" "); + for (String server : servers) { + try { + URI uri = new URI(server); + String host = new URI(server).getHost(); + + if (host == null || host.isEmpty()) { + logger.debug( + "Skipping a nameserver URI as host portion could not be extracted: {}", server); + // If the host portion can not be parsed we should just skip this entry. + continue; + } + int port = uri.getPort(); + defaultNameServers.add(SocketUtils.socketAddress(uri.getHost(), port == -1 ? + defaultPort : port)); + } catch (URISyntaxException e) { + logger.debug("Skipping a malformed nameserver URI: {}", server, e); + } + } + } + } catch (NamingException ignore) { + // Will try reflection if this fails. + } + } +} From b6d6d98404f216602e0dec8efd83e140c5ff7e00 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 14 Dec 2018 21:33:38 +0100 Subject: [PATCH 316/417] Skip tests that use KeyManagerFactory if not supported by OpenSSL version / flavor (#8662) Motivation: We missed to skip a few tests that depend on the KeyManagerFactory if the used OpenSSL version / flavor not support it. Modifications: Add missing overrides. Result: Testsuite also passes for example when using LibreSSL. --- .../handler/ssl/JdkOpenSslEngineInteroptTest.java | 14 ++++++++++++++ .../ssl/OpenSslJdkSslEngineInteroptTest.java | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java index a85b665ff2cc..45b3c7e5e088 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java @@ -100,6 +100,20 @@ public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth() thr super.testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(); } + @Override + @Test + public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testSessionAfterHandshakeKeyManagerFactoryMutualAuth(); + } + + @Override + @Test + public void testSessionAfterHandshakeKeyManagerFactory() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testSessionAfterHandshakeKeyManagerFactory(); + } + @Override protected void mySetupMutualAuthServerInitSslHandler(SslHandler handler) { ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) handler.engine(); diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java index bc1106e7a345..df4e757e505b 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java @@ -112,6 +112,13 @@ public void testClientHostnameValidationFail() throws InterruptedException, SSLE super.testClientHostnameValidationFail(); } + @Override + @Test + public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testSessionAfterHandshakeKeyManagerFactoryMutualAuth(); + } + @Override protected boolean mySetupMutualAuthServerIsValidServerException(Throwable cause) { // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. From 35f609ba61f64f9a0f868b7b7b84d924c88bf27f Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 17 Dec 2018 10:24:54 +0100 Subject: [PATCH 317/417] Update to latest stable jython release (#8667) Motivation: Using the latest jython release fixes some noise that is produced by an exception that is thrown when jython is terminated. Exception in thread "Jython-Netty-Client-4" Exception in thread "Jython-Netty-Client-7" Exception in thread "Jython-Netty-Client-5" java.lang.NoClassDefFoundError: org/python/netty/util/concurrent/DefaultPromise$2 at org.python.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:589) at org.python.netty.util.concurrent.DefaultPromise.setSuccess(DefaultPromise.java:397) at org.python.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:151) at java.lang.Thread.run(Thread.java:748) Exception in thread "Jython-Netty-Client-8" java.lang.NoClassDefFoundError: org/python/netty/util/concurrent/DefaultPromise$2 at org.python.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:589) at org.python.netty.util.concurrent.DefaultPromise.setSuccess(DefaultPromise.java:397) at org.python.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:151) at java.lang.Thread.run(Thread.java:748) Exception in thread "Jython-Netty-Client-3" java.lang.NoClassDefFoundError: org/python/netty/util/concurrent/DefaultPromise$2 at org.python.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:589) at org.python.netty.util.concurrent.DefaultPromise.setSuccess(DefaultPromise.java:397)% Modification: Update to latest stable release. Result: Less noise during build. --- testsuite-autobahn/pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 01cb2747dbae..705bb98a720a 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -88,6 +88,13 @@ + + + org.python + jython-standalone + 2.7.1 + + From de38d7513785a30e32e98b11376d5427a8cc45f0 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 17 Dec 2018 17:25:22 +0100 Subject: [PATCH 318/417] Upgrade to new version of autobahntestsuite maven plugin. (#8668) Motivation: A new version was released that fixes a few test-cases to allow more close codes. Modifications: Upgrade to 0.1.5 Result: More compliant testing of websockets. --- testsuite-autobahn/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 705bb98a720a..26bf5ce6f3db 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -70,7 +70,7 @@ me.normanmaurer.maven.autobahntestsuite autobahntestsuite-maven-plugin - 0.1.4 + 0.1.5 io.netty.testsuite.autobahn.AutobahnServer From 302dac8c45266bba54bea59823a5757a496f5fdc Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Mon, 17 Dec 2018 19:42:50 +0100 Subject: [PATCH 319/417] Support 1012, 1013 and 1014 WebSocket close status code (#8664) Motivation: RFC 6455 doesn't define close status codes 1012, 1013 and 1014. Yet, since then, IANA has defined them and web browsers support them. From https://www.iana.org/assignments/websocket/websocket.xhtml: * 1012: Service Restart * 1013: Try Again Later * 1014: The server was acting as a gateway or proxy and received an invalid response from the upstream server. This is similar to 502 HTTP Status Code. Modification: Make status codes 1012, 1013 and 1014 legit. Result: WebSocket status codes as defined by IANA are supported. --- .../websocketx/WebSocket08FrameDecoder.java | 2 +- .../WebSocket08FrameDecoderTest.java | 51 +++++++++++++++++-- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java index 604746c5ae2f..f5a6dc193600 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java @@ -451,7 +451,7 @@ protected void checkCloseFrameBody( // Must have 2 byte integer within the valid range int statusCode = buffer.readShort(); if (statusCode >= 0 && statusCode <= 999 || statusCode >= 1004 && statusCode <= 1006 - || statusCode >= 1012 && statusCode <= 2999) { + || statusCode >= 1015 && statusCode <= 2999) { protocolViolation(ctx, "Invalid close frame getStatus code: " + statusCode); } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java index 547eed6174e8..cb6c187efcf2 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java @@ -12,18 +12,61 @@ */ package io.netty.handler.codec.http.websocketx; +import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; - +import io.netty.channel.embedded.EmbeddedChannel; import org.junit.Test; -import org.mockito.Mockito; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; public class WebSocket08FrameDecoderTest { @Test public void channelInactive() throws Exception { final WebSocket08FrameDecoder decoder = new WebSocket08FrameDecoder(true, true, 65535, false); - final ChannelHandlerContext ctx = Mockito.mock(ChannelHandlerContext.class); + final ChannelHandlerContext ctx = mock(ChannelHandlerContext.class); decoder.channelInactive(ctx); - Mockito.verify(ctx).fireChannelInactive(); + verify(ctx).fireChannelInactive(); + } + + @Test + public void supportIanaStatusCodes() throws Exception { + Set forbiddenIanaCodes = new HashSet(); + forbiddenIanaCodes.add(1004); + forbiddenIanaCodes.add(1005); + forbiddenIanaCodes.add(1006); + Set validIanaCodes = new HashSet(); + for (int i = 1000; i < 1015; i++) { + validIanaCodes.add(i); + } + validIanaCodes.removeAll(forbiddenIanaCodes); + + for (int statusCode: validIanaCodes) { + EmbeddedChannel encoderChannel = new EmbeddedChannel(new WebSocket08FrameEncoder(true)); + EmbeddedChannel decoderChannel = new EmbeddedChannel(new WebSocket08FrameDecoder(true, true, 65535, false)); + + assertTrue(encoderChannel.writeOutbound(new CloseWebSocketFrame(statusCode, "Bye"))); + assertTrue(encoderChannel.finish()); + ByteBuf serializedCloseFrame = encoderChannel.readOutbound(); + assertNull(encoderChannel.readOutbound()); + + assertTrue(decoderChannel.writeInbound(serializedCloseFrame)); + assertTrue(decoderChannel.finish()); + + CloseWebSocketFrame outputFrame = decoderChannel.readInbound(); + assertNull(decoderChannel.readOutbound()); + try { + assertEquals(statusCode, outputFrame.statusCode()); + } finally { + outputFrame.release(); + } + } } } From db3c76ed72325076570e4ce5418dff323951cb7b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 19 Dec 2018 11:28:43 +0100 Subject: [PATCH 320/417] Update to use OpenJDK 12 EA24 when building with Java 12 (#8672) Motivation: A new EA build was released for Java 12. Modifications: Update to OpenJDK 12 EA24 Result: Use latest OpenJDK 12 build when building with Java 12 --- docker/docker-compose.centos-6.112.yaml | 2 +- docker/docker-compose.centos-7.112.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.112.yaml b/docker/docker-compose.centos-6.112.yaml index c49744ca717e..cc7f3bb5697a 100644 --- a/docker/docker-compose.centos-6.112.yaml +++ b/docker/docker-compose.centos-6.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "openjdk@1.12.0-22" + java_version : "openjdk@1.12.0-24" test: image: netty:centos-6-1.12 diff --git a/docker/docker-compose.centos-7.112.yaml b/docker/docker-compose.centos-7.112.yaml index 93c05349b96e..1c009cb94b3c 100644 --- a/docker/docker-compose.centos-7.112.yaml +++ b/docker/docker-compose.centos-7.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "openjdk@1.12.0-22" + java_version : "openjdk@1.12.0-24" test: image: netty:centos-7-1.12 From d77bdeaa7d8020d2d8cfe6357f4c824dce3e99f9 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 19 Dec 2018 12:13:56 +0100 Subject: [PATCH 321/417] Fix ClassCastException and native crash when using kqueue transport. (#8665) Motivation: How we did the mapping from native code to AbstractKQueueChannel was not safe and could lead to heap corruption. This then sometimes produced ClassCastExceptions or could also lead to crashes. This happened sometimes when running the testsuite. Modifications: Use a Map for the mapping (just as we do in the native epoll transport). Result: No more heap corruption / crashes. --- .../src/main/c/netty_kqueue_eventarray.c | 88 ++----------------- .../channel/kqueue/AbstractKQueueChannel.java | 12 +-- .../channel/kqueue/KQueueEventArray.java | 11 +-- .../netty/channel/kqueue/KQueueEventLoop.java | 40 +++------ 4 files changed, 23 insertions(+), 128 deletions(-) diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c b/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c index 0fb0c4cffd77..dd38cf544c75 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c @@ -24,104 +24,26 @@ #include "netty_unix_jni.h" #include "netty_unix_util.h" -static jfieldID kqueueJniPtrFieldId = NULL; - -static void netty_kqueue_eventarray_evSet(JNIEnv* env, jclass clzz, jlong keventAddress, jobject channel, jint ident, jshort filter, jshort flags, jint fflags) { - // Create a global pointer, cast it as a long, and retain it in java to re-use and free later. - jlong jniSelfPtr = (*env)->GetLongField(env, channel, kqueueJniPtrFieldId); - if (jniSelfPtr == 0) { - jniSelfPtr = (jlong) (*env)->NewGlobalRef(env, channel); - (*env)->SetLongField(env, channel, kqueueJniPtrFieldId, jniSelfPtr); - } else if ((flags & EV_DELETE) != 0) { - // If the event is deleted, make sure it no longer has a reference to the jniSelfPtr because it shouldn't be used after this point. - jniSelfPtr = 0; - } - EV_SET((struct kevent*) keventAddress, ident, filter, flags, fflags, 0, (jobject) jniSelfPtr); -} - -static jobject netty_kqueue_eventarray_getChannel(JNIEnv* env, jclass clazz, jlong keventAddress) { - struct kevent* event = (struct kevent*) keventAddress; - return event->udata == NULL ? NULL : (jobject) event->udata; -} - -static void netty_kqueue_eventarray_deleteGlobalRefs(JNIEnv* env, jclass clazz, jlong channelAddressStart, jlong channelAddressEnd) { - // Iterate over an array of longs, which are really pointers to the jobject NewGlobalRef created above in evSet - // and delete each one. The field has already been set to 0 in java. - jlong* itr = (jlong*) channelAddressStart; - const jlong* end = (jlong*) channelAddressEnd; - for (; itr != end; ++itr) { - (*env)->DeleteGlobalRef(env, (jobject) *itr); - } +static void netty_kqueue_eventarray_evSet(JNIEnv* env, jclass clzz, jlong keventAddress, jint ident, jshort filter, jshort flags, jint fflags) { + EV_SET((struct kevent*) keventAddress, ident, filter, flags, fflags, 0, NULL); } // JNI Method Registration Table Begin static const JNINativeMethod fixed_method_table[] = { - { "deleteGlobalRefs", "(JJ)V", (void *) netty_kqueue_eventarray_deleteGlobalRefs } - // "evSet" has a dynamic signature - // "getChannel" has a dynamic signature + { "evSet", "(JISSI)V", (void *) netty_kqueue_eventarray_evSet } }; static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]); -static jint dynamicMethodsTableSize() { - return fixed_method_table_size + 2; -} - -static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { - JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize()); - memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table)); - char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/kqueue/AbstractKQueueChannel;ISSI)V"); - JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size]; - dynamicMethod->name = "evSet"; - dynamicMethod->signature = netty_unix_util_prepend("(JL", dynamicTypeName); - dynamicMethod->fnPtr = (void *) netty_kqueue_eventarray_evSet; - free(dynamicTypeName); - - ++dynamicMethod; - dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/kqueue/AbstractKQueueChannel;"); - dynamicMethod->name = "getChannel"; - dynamicMethod->signature = netty_unix_util_prepend("(J)L", dynamicTypeName); - dynamicMethod->fnPtr = (void *) netty_kqueue_eventarray_getChannel; - free(dynamicTypeName); - return dynamicMethods; -} - -static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) { - jint fullMethodTableSize = dynamicMethodsTableSize(); - jint i = fixed_method_table_size; - for (; i < fullMethodTableSize; ++i) { - free(dynamicMethods[i].signature); - } - free(dynamicMethods); -} // JNI Method Registration Table End jint netty_kqueue_eventarray_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { - JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix); if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/kqueue/KQueueEventArray", - dynamicMethods, - dynamicMethodsTableSize()) != 0) { - freeDynamicMethodsTable(dynamicMethods); - return JNI_ERR; - } - freeDynamicMethodsTable(dynamicMethods); - dynamicMethods = NULL; - - char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/kqueue/AbstractKQueueChannel"); - jclass kqueueChannelCls = (*env)->FindClass(env, nettyClassName); - free(nettyClassName); - nettyClassName = NULL; - if (kqueueChannelCls == NULL) { + fixed_method_table, + fixed_method_table_size) != 0) { return JNI_ERR; } - - kqueueJniPtrFieldId = (*env)->GetFieldID(env, kqueueChannelCls, "jniSelfPtr", "J"); - if (kqueueJniPtrFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: AbstractKQueueChannel.jniSelfPtr"); - return JNI_ERR; - } - return NETTY_JNI_VERSION; } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java index 6073fa9ca120..559999ce7eeb 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java @@ -69,15 +69,6 @@ abstract class AbstractKQueueChannel extends AbstractChannel implements UnixChan private boolean writeFilterEnabled; boolean readReadyRunnablePending; boolean inputClosedSeenErrorOnRead; - /** - * This member variable means we don't have to have a map in {@link KQueueEventLoop} which associates the FDs - * from kqueue to instances of this class. This field will be initialized by JNI when modifying kqueue events. - * If there is no global reference when JNI gets a kqueue evSet call (aka this field is 0) then a global reference - * will be created and the address will be saved in this member variable. Then when we process a kevent in Java - * we can ask JNI to give us the {@link AbstractKQueueChannel} that corresponds to that event. - */ - long jniSelfPtr; - protected volatile boolean active; private volatile SocketAddress local; private volatile SocketAddress remote; @@ -213,6 +204,9 @@ protected void doRegister() throws Exception { // make sure the readReadyRunnablePending variable is reset so we will be able to execute the Runnable on the // new EventLoop. readReadyRunnablePending = false; + + ((KQueueEventLoop) eventLoop()).add(this); + // Add the write event first so we get notified of connection refused on the client side! if (writeFilterEnabled) { evSet0(Native.EVFILT_WRITE, Native.EV_ADD_CLEAR_ENABLE); diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventArray.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventArray.java index 4a263280a680..43b5e6266309 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventArray.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventArray.java @@ -79,7 +79,7 @@ void clear() { void evSet(AbstractKQueueChannel ch, short filter, short flags, int fflags) { reallocIfNeeded(); - evSet(getKEventOffset(size++) + memoryAddress, ch, ch.socket.intValue(), filter, flags, fflags); + evSet(getKEventOffset(size++) + memoryAddress, ch.socket.intValue(), filter, flags, fflags); } private void reallocIfNeeded() { @@ -165,16 +165,9 @@ long data(int index) { return memory.getLong(getKEventOffset(index) + KQUEUE_DATA_OFFSET); } - AbstractKQueueChannel channel(int index) { - return getChannel(getKEventOffsetAddress(index)); - } - private static int calculateBufferCapacity(int capacity) { return capacity * KQUEUE_EVENT_SIZE; } - private static native void evSet(long keventAddress, AbstractKQueueChannel ch, - int ident, short filter, short flags, int fflags); - private static native AbstractKQueueChannel getChannel(long keventAddress); - static native void deleteGlobalRefs(long channelAddressStart, long channelAddressEnd); + private static native void evSet(long keventAddress, int ident, short filter, short flags, int fflags); } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java index 8f2a4ca45d53..25c50a4f05ba 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueEventLoop.java @@ -23,6 +23,8 @@ import io.netty.channel.unix.FileDescriptor; import io.netty.channel.unix.IovArray; import io.netty.util.IntSupplier; +import io.netty.util.collection.IntObjectHashMap; +import io.netty.util.collection.IntObjectMap; import io.netty.util.concurrent.RejectedExecutionHandler; import io.netty.util.internal.ObjectUtil; import io.netty.util.internal.PlatformDependent; @@ -31,11 +33,9 @@ import java.io.IOException; import java.util.Queue; -import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import static io.netty.channel.kqueue.KQueueEventArray.deleteGlobalRefs; import static java.lang.Math.min; /** @@ -53,7 +53,6 @@ final class KQueueEventLoop extends SingleThreadEventLoop { KQueue.ensureAvailability(); } - private final NativeLongArray jniChannelPointers; private final boolean allowGrowing; private final FileDescriptor kqueueFd; private final KQueueEventArray changeList; @@ -66,6 +65,7 @@ public int get() throws Exception { return kqueueWaitNow(); } }; + private final IntObjectMap channels = new IntObjectHashMap(4096); private volatile int wakenUp; private volatile int ioRatio = 50; @@ -83,7 +83,6 @@ public int get() throws Exception { } changeList = new KQueueEventArray(maxEvents); eventList = new KQueueEventArray(maxEvents); - jniChannelPointers = new NativeLongArray(4096); int result = Native.keventAddUserEvent(kqueueFd.intValue(), KQUEUE_WAKE_UP_IDENT); if (result < 0) { cleanup(); @@ -91,18 +90,18 @@ public int get() throws Exception { } } + void add(AbstractKQueueChannel ch) { + assert inEventLoop(); + channels.put(ch.fd().intValue(), ch); + } + void evSet(AbstractKQueueChannel ch, short filter, short flags, int fflags) { changeList.evSet(ch, filter, flags, fflags); } - void remove(AbstractKQueueChannel ch) throws IOException { + void remove(AbstractKQueueChannel ch) { assert inEventLoop(); - if (ch.jniSelfPtr == 0) { - return; - } - - jniChannelPointers.add(ch.jniSelfPtr); - ch.jniSelfPtr = 0; + channels.remove(ch.fd().intValue()); } /** @@ -145,32 +144,25 @@ private int kqueueWaitNow() throws IOException { } private int kqueueWait(int timeoutSec, int timeoutNs) throws IOException { - deleteJniChannelPointers(); int numEvents = Native.keventWait(kqueueFd.intValue(), changeList, eventList, timeoutSec, timeoutNs); changeList.clear(); return numEvents; } - private void deleteJniChannelPointers() { - if (!jniChannelPointers.isEmpty()) { - deleteGlobalRefs(jniChannelPointers.memoryAddress(), jniChannelPointers.memoryAddressEnd()); - jniChannelPointers.clear(); - } - } - private void processReady(int ready) { for (int i = 0; i < ready; ++i) { final short filter = eventList.filter(i); final short flags = eventList.flags(i); + final int fd = eventList.fd(i); if (filter == Native.EVFILT_USER || (flags & Native.EV_ERROR) != 0) { // EV_ERROR is returned if the FD is closed synchronously (which removes from kqueue) and then // we later attempt to delete the filters from kqueue. assert filter != Native.EVFILT_USER || - (filter == Native.EVFILT_USER && eventList.fd(i) == KQUEUE_WAKE_UP_IDENT); + (filter == Native.EVFILT_USER && fd == KQUEUE_WAKE_UP_IDENT); continue; } - AbstractKQueueChannel channel = eventList.channel(i); + AbstractKQueueChannel channel = channels.get(fd); if (channel == null) { // This may happen if the channel has already been closed, and it will be removed from kqueue anyways. // We also handle EV_ERROR above to skip this even early if it is a result of a referencing a closed and @@ -327,12 +319,6 @@ protected void cleanup() { } } finally { // Cleanup all native memory! - - // The JNI channel pointers should already be deleted because we should wait on kevent before this method, - // but lets just be sure we cleanup native memory. - deleteJniChannelPointers(); - jniChannelPointers.free(); - changeList.free(); eventList.free(); } From 9947df4a748c6ef1df50434aded3ecdda7ce6d57 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 19 Dec 2018 12:55:48 +0100 Subject: [PATCH 322/417] =?UTF-8?q?Add=20test=20for=20correctly=20handling?= =?UTF-8?q?=20SSLSessionBindingEvent=20when=20acting=20on=20th=E2=80=A6=20?= =?UTF-8?q?(#8649)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: During some other work I noticed we do not have any tests to ensure we correctly use SSLSessionBindingEvent. We should add some testing. Modifications: - Added unit test to verify we correctly implement it. - Ignore the test when using Conscrypt as it not correctly implements it. Result: More tests for custom SSL impl. --- .../ssl/ConscryptJdkSslEngineInteropTest.java | 7 ++ .../handler/ssl/ConscryptSslEngineTest.java | 7 ++ .../io/netty/handler/ssl/SSLEngineTest.java | 82 +++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java index d666535b194b..0976264d7a12 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java @@ -79,4 +79,11 @@ protected boolean mySetupMutualAuthServerIsValidServerException(Throwable cause) // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. return super.mySetupMutualAuthServerIsValidServerException(cause) || causedBySSLException(cause); } + + @Ignore("Ignore due bug in Conscrypt") + @Override + public void testSessionBindingEvent() throws Exception { + // Ignore due bug in Conscrypt where the incorrect SSLSession object is used in the SSLSessionBindingEvent. + // See https://github.com/google/conscrypt/issues/593 + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java index 8c6121b6fb59..7d068409905b 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java @@ -77,4 +77,11 @@ public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth() { @Override public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth() { } + + @Ignore("Ignore due bug in Conscrypt") + @Override + public void testSessionBindingEvent() throws Exception { + // Ignore due bug in Conscrypt where the incorrect SSLSession object is used in the SSLSessionBindingEvent. + // See https://github.com/google/conscrypt/issues/593 + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index a006fad7207f..c1a4e12056b9 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -86,6 +86,8 @@ import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSessionBindingEvent; +import javax.net.ssl.SSLSessionBindingListener; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; @@ -2730,6 +2732,86 @@ public void testGetCiphersuite() throws Exception { } } + @Test + public void testSessionBindingEvent() throws Exception { + clientSslCtx = SslContextBuilder.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(sslClientProvider()) + .sslContextProvider(clientSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); + SelfSignedCertificate ssc = new SelfSignedCertificate(); + serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) + .sslProvider(sslServerProvider()) + .sslContextProvider(serverSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); + SSLEngine clientEngine = null; + SSLEngine serverEngine = null; + try { + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + handshake(clientEngine, serverEngine); + SSLSession session = clientEngine.getSession(); + assertEquals(0, session.getValueNames().length); + + class SSLSessionBindingEventValue implements SSLSessionBindingListener { + SSLSessionBindingEvent boundEvent; + SSLSessionBindingEvent unboundEvent; + + @Override + public void valueBound(SSLSessionBindingEvent sslSessionBindingEvent) { + assertNull(boundEvent); + boundEvent = sslSessionBindingEvent; + } + + @Override + public void valueUnbound(SSLSessionBindingEvent sslSessionBindingEvent) { + assertNull(unboundEvent); + unboundEvent = sslSessionBindingEvent; + } + } + + String name = "name"; + String name2 = "name2"; + + SSLSessionBindingEventValue value1 = new SSLSessionBindingEventValue(); + session.putValue(name, value1); + assertSSLSessionBindingEventValue(name, session, value1.boundEvent); + assertNull(value1.unboundEvent); + assertEquals(1, session.getValueNames().length); + + session.putValue(name2, "value"); + + SSLSessionBindingEventValue value2 = new SSLSessionBindingEventValue(); + session.putValue(name, value2); + assertEquals(2, session.getValueNames().length); + + assertSSLSessionBindingEventValue(name, session, value1.unboundEvent); + assertSSLSessionBindingEventValue(name, session, value2.boundEvent); + assertNull(value2.unboundEvent); + assertEquals(2, session.getValueNames().length); + + session.removeValue(name); + assertSSLSessionBindingEventValue(name, session, value2.unboundEvent); + assertEquals(1, session.getValueNames().length); + session.removeValue(name2); + } finally { + cleanupClientSslEngine(clientEngine); + cleanupServerSslEngine(serverEngine); + ssc.delete(); + } + } + + private static void assertSSLSessionBindingEventValue( + String name, SSLSession session, SSLSessionBindingEvent event) { + assertEquals(name, event.getName()); + assertEquals(session, event.getSession()); + assertEquals(session, event.getSource()); + } + @Test public void testSessionAfterHandshake() throws Exception { testSessionAfterHandshake0(false, false); From e2d9665707f6a6bb97e88c57dd463f296c28b190 Mon Sep 17 00:00:00 2001 From: Alex Vasiliev Date: Thu, 20 Dec 2018 17:40:06 +1100 Subject: [PATCH 323/417] Added comments to LineBasedFrameDecoder, JsonObjectDecoder and XmlFrameDecoder that they are only compatible with UTF-8 encoded streams. (#8651) Motivation: LineBasedFrameDecoder, JsonObjectDecoder and XmlFrameDecoder upon investigation of the sourcecode appeared to only support ASCII or UTF-8 input. It is an important characteristic and ont reflected in any documentation. This could lead to improper usage and bugs. Modifications: Javadoc comment is addedd to all three classes to state that implementation is only compatible with UTF-8 or ASCII input streams and brifly touches on implementaion details. Result: The end user of the netty library would not have to study sorcecode to deterime character encoding limitations for given classes. --- .../java/io/netty/handler/codec/LineBasedFrameDecoder.java | 6 ++++++ .../io/netty/handler/codec/json/JsonObjectDecoder.java | 7 ++++++- .../java/io/netty/handler/codec/xml/XmlFrameDecoder.java | 6 ++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java index 58f2e85f04cd..fda45cc401ec 100644 --- a/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java @@ -25,6 +25,12 @@ * A decoder that splits the received {@link ByteBuf}s on line endings. *

* Both {@code "\n"} and {@code "\r\n"} are handled. + *

+ * The byte stream is expected to be in UTF-8 character encoding or ASCII. The current implementation + * uses direct {@code byte} to {@code char} cast and then compares that {@code char} to a few low range + * ASCII characters like {@code '\n'} or {@code '\r'}. UTF-8 is not using low range [0..0x7F] + * byte values for multibyte codepoint representations therefore fully supported by this implementation. + *

* For a more general delimiter-based decoder, see {@link DelimiterBasedFrameDecoder}. */ public class LineBasedFrameDecoder extends ByteToMessageDecoder { diff --git a/codec/src/main/java/io/netty/handler/codec/json/JsonObjectDecoder.java b/codec/src/main/java/io/netty/handler/codec/json/JsonObjectDecoder.java index f63e62066ee0..2508ff6c09c1 100644 --- a/codec/src/main/java/io/netty/handler/codec/json/JsonObjectDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/json/JsonObjectDecoder.java @@ -30,7 +30,12 @@ /** * Splits a byte stream of JSON objects and arrays into individual objects/arrays and passes them up the * {@link ChannelPipeline}. - * + *

+ * The byte stream is expected to be in UTF-8 character encoding or ASCII. The current implementation + * uses direct {@code byte} to {@code char} cast and then compares that {@code char} to a few low range + * ASCII characters like {@code '{'}, {@code '['} or {@code '"'}. UTF-8 is not using low range [0..0x7F] + * byte values for multibyte codepoint representations therefore fully supported by this implementation. + *

* This class does not do any real parsing or validation. A sequence of bytes is considered a JSON object/array * if it contains a matching number of opening and closing braces/brackets. It's up to a subsequent * {@link ChannelHandler} to parse the JSON text into a more usable form i.e. a POJO. diff --git a/codec/src/main/java/io/netty/handler/codec/xml/XmlFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/xml/XmlFrameDecoder.java index 4a8b262bf8a3..480f0be1c53e 100644 --- a/codec/src/main/java/io/netty/handler/codec/xml/XmlFrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/xml/XmlFrameDecoder.java @@ -59,6 +59,12 @@ * +-----------------+-------------------------------------+ * * + *

+ * The byte stream is expected to be in UTF-8 character encoding or ASCII. The current implementation + * uses direct {@code byte} to {@code char} cast and then compares that {@code char} to a few low range + * ASCII characters like {@code '<'}, {@code '>'} or {@code '/'}. UTF-8 is not using low range [0..0x7F] + * byte values for multibyte codepoint representations therefore fully supported by this implementation. + *

* Please note that this decoder is not suitable for * xml streaming protocols such as * XMPP, From 6464c98743779778e52db59522f269bfe47a46af Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 21 Dec 2018 11:06:43 +0100 Subject: [PATCH 324/417] =?UTF-8?q?Call=20FastThreadLocal.removeAll()=20be?= =?UTF-8?q?fore=20notify=20termination=20future=20of=20=E2=80=A6=20(#8666)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: We should try removing all FastThreadLocals for the Thread before we notify the termination. future. The user may block on the future and once it unblocks the JVM may terminate and start unloading classes. Modifications: Remove all FastThreadLocals for the Thread before notify termination future. Result: Fixes https://github.com/netty/netty/issues/6596. --- .../netty/util/concurrent/SingleThreadEventExecutor.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java index dab7e9500d01..56fd442b6667 100644 --- a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java +++ b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java @@ -939,6 +939,12 @@ public void run() { try { cleanup(); } finally { + // Lets remove all FastThreadLocals for the Thread as we are about to terminate and notify + // the future. The user may block on the future and once it unblocks the JVM may terminate + // and start unloading classes. + // See https://github.com/netty/netty/issues/6596. + FastThreadLocal.removeAll(); + STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED); threadLock.release(); if (!taskQueue.isEmpty()) { @@ -947,7 +953,6 @@ public void run() { "non-empty task queue (" + taskQueue.size() + ')'); } } - terminationFuture.setSuccess(null); } } From 66ccd1483a9f0c18b707711558c7bbea8bfc73da Mon Sep 17 00:00:00 2001 From: Jon Chambers Date: Tue, 25 Dec 2018 16:35:58 -0500 Subject: [PATCH 325/417] Publicize default `explicitFlushAfterFlushes` count. (#8683) Motivation: Users who want to construct a `FlushConsolidationHandler` with a default `explicitFlushAfterFlushes` but non-default `consolidateWhenNoReadInProgress` may benefit from having an easy way to get the default "flush after flushes" count. Modifications: - Moved default `explicitFlushAfterFlushes` value to a public constant. - Adjusted Javadoc accordingly. Result: Default `explicitFlushAfterFlushes` is accessible to callers. --- .../handler/flush/FlushConsolidationHandler.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/flush/FlushConsolidationHandler.java b/handler/src/main/java/io/netty/handler/flush/FlushConsolidationHandler.java index 12d31627f6ec..472a83bffcae 100644 --- a/handler/src/main/java/io/netty/handler/flush/FlushConsolidationHandler.java +++ b/handler/src/main/java/io/netty/handler/flush/FlushConsolidationHandler.java @@ -47,8 +47,8 @@ * high throughput, this gives the opportunity to process other flushes before the task gets executed, thus * batching multiple flushes into one. * - * If {@code explicitFlushAfterFlushes} is reached the flush will also be forwarded as well (whether while in a read - * loop, or while batching outside of a read loop). + * If {@code explicitFlushAfterFlushes} is reached the flush will be forwarded as well (whether while in a read loop, or + * while batching outside of a read loop). *

* If the {@link Channel} becomes non-writable it will also try to execute any pending flush operations. *

@@ -65,10 +65,17 @@ public class FlushConsolidationHandler extends ChannelDuplexHandler { private Future nextScheduledFlush; /** - * Create new instance which explicit flush after 256 pending flush operations latest. + * The default number of flushes after which a flush will be forwarded to downstream handlers (whether while in a + * read loop, or while batching outside of a read loop). + */ + public static final int DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES = 256; + + /** + * Create new instance which explicit flush after {@value DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES} pending flush + * operations at the latest. */ public FlushConsolidationHandler() { - this(256, false); + this(DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, false); } /** From fa84e2b3af45ec7fd47909eff0aa7d2be5a54972 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 14 Dec 2018 10:10:04 +0000 Subject: [PATCH 326/417] Cleanup HTTP/2 tests for Http2FrameCodec and Http2MultiplexCodec (#8646) Motiviation: Http2FrameCodecTest and Http2MultiplexCodecTest were quite fragile and often not went through the whole pipeline which made testing sometimes hard and error-prone. Modification: - Refactor tests to have data flow through the whole pipeline and so made the test more robust (by testing the while implementation). Result: Easier to write tests for the codecs in the future and more robust testing in general. Beside this it also fixes https://github.com/netty/netty/issues/6036. --- .../handler/codec/http2/Http2FrameCodec.java | 2 +- .../codec/http2/Http2MultiplexCodec.java | 23 +- .../http2/Http2MultiplexCodecBuilder.java | 29 + .../codec/http2/Http2FrameCodecTest.java | 203 ++-- .../codec/http2/Http2FrameInboundWriter.java | 340 +++++++ .../codec/http2/Http2MultiplexCodecTest.java | 879 ++++++++---------- .../handler/codec/http2/Http2TestUtil.java | 193 ++++ .../codec/http2/TestChannelInitializer.java | 4 +- 8 files changed, 1030 insertions(+), 643 deletions(-) create mode 100644 codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameInboundWriter.java diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java index 47f2533f3f81..b9075144f3e8 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java @@ -148,7 +148,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler { private final Integer initialFlowControlWindowSize; - private ChannelHandlerContext ctx; + ChannelHandlerContext ctx; /** Number of buffered streams if the {@link StreamBufferingEncoder} is used. **/ private int numBufferedStreams; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java index 46aa97a5360e..d19ce2b8f805 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java @@ -414,28 +414,11 @@ final void onChannelReadComplete(ChannelHandlerContext ctx) { } } - // Allow to override for testing - void flush0(ChannelHandlerContext ctx) { + final void flush0(ChannelHandlerContext ctx) { flush(ctx); } - /** - * Return bytes to flow control. - *

- * Package private to allow to override for testing - * @param ctx The {@link ChannelHandlerContext} associated with the parent channel. - * @param stream The object representing the HTTP/2 stream. - * @param bytes The number of bytes to return to flow control. - * @return {@code true} if a frame has been written as a result of this method call. - * @throws Http2Exception If this operation violates the flow control limits. - */ - boolean onBytesConsumed(@SuppressWarnings("unused") ChannelHandlerContext ctx, - Http2FrameStream stream, int bytes) throws Http2Exception { - return consumeBytes(stream.id(), bytes); - } - - // Allow to extend for testing - static class Http2MultiplexCodecStream extends DefaultHttp2FrameStream { + static final class Http2MultiplexCodecStream extends DefaultHttp2FrameStream { DefaultHttp2StreamChannel channel; } @@ -1084,7 +1067,7 @@ void doRead0(Http2Frame frame, Handle allocHandle) { allocHandle.lastBytesRead(numBytesToBeConsumed); if (numBytesToBeConsumed != 0) { try { - writeDoneAndNoFlush |= onBytesConsumed(ctx, stream, numBytesToBeConsumed); + writeDoneAndNoFlush |= consumeBytes(stream.id(), numBytesToBeConsumed); } catch (Http2Exception e) { pipeline().fireExceptionCaught(e); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java index 94f0d4972038..c5732ec68748 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java @@ -27,6 +27,7 @@ @UnstableApi public class Http2MultiplexCodecBuilder extends AbstractHttp2ConnectionHandlerBuilder { + private Http2FrameWriter frameWriter; final ChannelHandler childHandler; private ChannelHandler upgradeStreamHandler; @@ -44,6 +45,12 @@ private static ChannelHandler checkSharable(ChannelHandler handler) { return handler; } + // For testing only. + Http2MultiplexCodecBuilder frameWriter(Http2FrameWriter frameWriter) { + this.frameWriter = checkNotNull(frameWriter, "frameWriter"); + return this; + } + /** * Creates a builder for a HTTP/2 client. * @@ -160,6 +167,28 @@ public Http2MultiplexCodecBuilder initialHuffmanDecodeCapacity(int initialHuffma @Override public Http2MultiplexCodec build() { + Http2FrameWriter frameWriter = this.frameWriter; + if (frameWriter != null) { + // This is to support our tests and will never be executed by the user as frameWriter(...) + // is package-private. + DefaultHttp2Connection connection = new DefaultHttp2Connection(isServer(), maxReservedStreams()); + Long maxHeaderListSize = initialSettings().maxHeaderListSize(); + Http2FrameReader frameReader = new DefaultHttp2FrameReader(maxHeaderListSize == null ? + new DefaultHttp2HeadersDecoder(true) : + new DefaultHttp2HeadersDecoder(true, maxHeaderListSize)); + + if (frameLogger() != null) { + frameWriter = new Http2OutboundFrameLogger(frameWriter, frameLogger()); + frameReader = new Http2InboundFrameLogger(frameReader, frameLogger()); + } + Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, frameWriter); + if (encoderEnforceMaxConcurrentStreams()) { + encoder = new StreamBufferingEncoder(encoder); + } + Http2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, frameReader); + + return build(decoder, encoder, initialSettings()); + } return super.build(); } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java index 212398604723..a3ccf038d017 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java @@ -15,9 +15,7 @@ package io.netty.handler.codec.http2; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; @@ -57,6 +55,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid; +import static io.netty.handler.codec.http2.Http2TestUtil.anyChannelPromise; +import static io.netty.handler.codec.http2.Http2TestUtil.anyHttp2Settings; +import static io.netty.handler.codec.http2.Http2TestUtil.assertEqualsAndRelease; +import static io.netty.handler.codec.http2.Http2TestUtil.bb; + import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -73,7 +76,6 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.same; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -86,9 +88,10 @@ public class Http2FrameCodecTest { private Http2FrameWriter frameWriter; private Http2FrameCodec frameCodec; private EmbeddedChannel channel; + // For injecting inbound frames - private Http2FrameListener frameListener; - private ChannelHandlerContext http2HandlerCtx; + private Http2FrameInboundWriter frameInboundWriter; + private LastInboundHandler inboundHandler; private final Http2Headers request = new DefaultHttp2Headers() @@ -123,29 +126,29 @@ private void setUp(Http2FrameCodecBuilder frameCodecBuilder, Http2Settings initi */ tearDown(); - frameWriter = spy(new VerifiableHttp2FrameWriter()); + frameWriter = Http2TestUtil.mockedFrameWriter(); + frameCodec = frameCodecBuilder.frameWriter(frameWriter).frameLogger(new Http2FrameLogger(LogLevel.TRACE)) .initialSettings(initialRemoteSettings).build(); - frameListener = ((DefaultHttp2ConnectionDecoder) frameCodec.decoder()) - .internalFrameListener(); inboundHandler = new LastInboundHandler(); channel = new EmbeddedChannel(); + frameInboundWriter = new Http2FrameInboundWriter(channel); channel.connect(new InetSocketAddress(0)); channel.pipeline().addLast(frameCodec); channel.pipeline().addLast(inboundHandler); channel.pipeline().fireChannelActive(); - http2HandlerCtx = channel.pipeline().context(frameCodec); - // Handshake - verify(frameWriter).writeSettings(eq(http2HandlerCtx), - anyHttp2Settings(), anyChannelPromise()); + verify(frameWriter).writeSettings(eqFrameCodecCtx(), anyHttp2Settings(), anyChannelPromise()); verifyNoMoreInteractions(frameWriter); channel.writeInbound(Http2CodecUtil.connectionPrefaceBuf()); - frameListener.onSettingsRead(http2HandlerCtx, initialRemoteSettings); - verify(frameWriter).writeSettingsAck(eq(http2HandlerCtx), anyChannelPromise()); - frameListener.onSettingsAckRead(http2HandlerCtx); + + frameInboundWriter.writeInboundSettings(initialRemoteSettings); + + verify(frameWriter).writeSettingsAck(eqFrameCodecCtx(), anyChannelPromise()); + + frameInboundWriter.writeInboundSettingsAck(); Http2SettingsFrame settingsFrame = inboundHandler.readInbound(); assertNotNull(settingsFrame); @@ -153,7 +156,7 @@ private void setUp(Http2FrameCodecBuilder frameCodecBuilder, Http2Settings initi @Test public void stateChanges() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 1, request, 31, true); + frameInboundWriter.writeInboundHeaders(1, request, 31, true); Http2Stream stream = frameCodec.connection().stream(1); assertNotNull(stream); @@ -169,12 +172,12 @@ public void stateChanges() throws Exception { assertEquals(inboundFrame, new DefaultHttp2HeadersFrame(request, true, 31).stream(stream2)); assertNull(inboundHandler.readInbound()); - inboundHandler.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2)); + channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2)); verify(frameWriter).writeHeaders( - eq(http2HandlerCtx), eq(1), eq(response), anyInt(), anyShort(), anyBoolean(), + eqFrameCodecCtx(), eq(1), eq(response), anyInt(), anyShort(), anyBoolean(), eq(27), eq(true), anyChannelPromise()); verify(frameWriter, never()).writeRstStream( - any(ChannelHandlerContext.class), anyInt(), anyLong(), anyChannelPromise()); + eqFrameCodecCtx(), anyInt(), anyLong(), anyChannelPromise()); assertEquals(State.CLOSED, stream.state()); event = inboundHandler.readInboundMessageOrUserEvent(); @@ -185,7 +188,7 @@ public void stateChanges() throws Exception { @Test public void headerRequestHeaderResponse() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 1, request, 31, true); + frameInboundWriter.writeInboundHeaders(1, request, 31, true); Http2Stream stream = frameCodec.connection().stream(1); assertNotNull(stream); @@ -198,34 +201,34 @@ public void headerRequestHeaderResponse() throws Exception { assertEquals(inboundFrame, new DefaultHttp2HeadersFrame(request, true, 31).stream(stream2)); assertNull(inboundHandler.readInbound()); - inboundHandler.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2)); + channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2)); verify(frameWriter).writeHeaders( - eq(http2HandlerCtx), eq(1), eq(response), anyInt(), anyShort(), anyBoolean(), + eqFrameCodecCtx(), eq(1), eq(response), anyInt(), anyShort(), anyBoolean(), eq(27), eq(true), anyChannelPromise()); verify(frameWriter, never()).writeRstStream( - any(ChannelHandlerContext.class), anyInt(), anyLong(), anyChannelPromise()); + eqFrameCodecCtx(), anyInt(), anyLong(), anyChannelPromise()); assertEquals(State.CLOSED, stream.state()); assertTrue(channel.isActive()); } - @Test - public void flowControlShouldBeResilientToMissingStreams() throws Http2Exception { - Http2Connection conn = new DefaultHttp2Connection(true); - Http2ConnectionEncoder enc = new DefaultHttp2ConnectionEncoder(conn, new DefaultHttp2FrameWriter()); - Http2ConnectionDecoder dec = new DefaultHttp2ConnectionDecoder(conn, enc, new DefaultHttp2FrameReader()); - Http2FrameCodec codec = new Http2FrameCodec(enc, dec, new Http2Settings()); - EmbeddedChannel em = new EmbeddedChannel(codec); + @Test + public void flowControlShouldBeResilientToMissingStreams() throws Http2Exception { + Http2Connection conn = new DefaultHttp2Connection(true); + Http2ConnectionEncoder enc = new DefaultHttp2ConnectionEncoder(conn, new DefaultHttp2FrameWriter()); + Http2ConnectionDecoder dec = new DefaultHttp2ConnectionDecoder(conn, enc, new DefaultHttp2FrameReader()); + Http2FrameCodec codec = new Http2FrameCodec(enc, dec, new Http2Settings()); + EmbeddedChannel em = new EmbeddedChannel(codec); - // We call #consumeBytes on a stream id which has not been seen yet to emulate the case - // where a stream is deregistered which in reality can happen in response to a RST. - assertFalse(codec.consumeBytes(1, 1)); - assertTrue(em.finishAndReleaseAll()); - } + // We call #consumeBytes on a stream id which has not been seen yet to emulate the case + // where a stream is deregistered which in reality can happen in response to a RST. + assertFalse(codec.consumeBytes(1, 1)); + assertTrue(em.finishAndReleaseAll()); + } @Test public void entityRequestEntityResponse() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 1, request, 0, false); + frameInboundWriter.writeInboundHeaders(1, request, 0, false); Http2Stream stream = frameCodec.connection().stream(1); assertNotNull(stream); @@ -239,39 +242,35 @@ public void entityRequestEntityResponse() throws Exception { assertNull(inboundHandler.readInbound()); ByteBuf hello = bb("hello"); - frameListener.onDataRead(http2HandlerCtx, 1, hello, 31, true); - // Release hello to emulate ByteToMessageDecoder - hello.release(); + frameInboundWriter.writeInboundData(1, hello, 31, true); Http2DataFrame inboundData = inboundHandler.readInbound(); Http2DataFrame expected = new DefaultHttp2DataFrame(bb("hello"), true, 31).stream(stream2); - assertEquals(expected, inboundData); + assertEqualsAndRelease(expected, inboundData); - assertEquals(1, inboundData.refCnt()); - expected.release(); - inboundData.release(); assertNull(inboundHandler.readInbound()); - inboundHandler.writeOutbound(new DefaultHttp2HeadersFrame(response, false).stream(stream2)); - verify(frameWriter).writeHeaders(eq(http2HandlerCtx), eq(1), eq(response), anyInt(), + channel.writeOutbound(new DefaultHttp2HeadersFrame(response, false).stream(stream2)); + verify(frameWriter).writeHeaders(eqFrameCodecCtx(), eq(1), eq(response), anyInt(), anyShort(), anyBoolean(), eq(0), eq(false), anyChannelPromise()); - inboundHandler.writeOutbound(new DefaultHttp2DataFrame(bb("world"), true, 27).stream(stream2)); + channel.writeOutbound(new DefaultHttp2DataFrame(bb("world"), true, 27).stream(stream2)); ArgumentCaptor outboundData = ArgumentCaptor.forClass(ByteBuf.class); - verify(frameWriter).writeData(eq(http2HandlerCtx), eq(1), outboundData.capture(), eq(27), + verify(frameWriter).writeData(eqFrameCodecCtx(), eq(1), outboundData.capture(), eq(27), eq(true), anyChannelPromise()); ByteBuf bb = bb("world"); assertEquals(bb, outboundData.getValue()); assertEquals(1, outboundData.getValue().refCnt()); bb.release(); - verify(frameWriter, never()).writeRstStream( - any(ChannelHandlerContext.class), anyInt(), anyLong(), anyChannelPromise()); + outboundData.getValue().release(); + + verify(frameWriter, never()).writeRstStream(eqFrameCodecCtx(), anyInt(), anyLong(), anyChannelPromise()); assertTrue(channel.isActive()); } @Test public void sendRstStream() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 3, request, 31, true); + frameInboundWriter.writeInboundHeaders(3, request, 31, true); Http2Stream stream = frameCodec.connection().stream(3); assertNotNull(stream); @@ -285,16 +284,15 @@ public void sendRstStream() throws Exception { assertNotNull(stream2); assertEquals(3, stream2.id()); - inboundHandler.writeOutbound(new DefaultHttp2ResetFrame(314 /* non-standard error */).stream(stream2)); - verify(frameWriter).writeRstStream( - eq(http2HandlerCtx), eq(3), eq(314L), anyChannelPromise()); + channel.writeOutbound(new DefaultHttp2ResetFrame(314 /* non-standard error */).stream(stream2)); + verify(frameWriter).writeRstStream(eqFrameCodecCtx(), eq(3), eq(314L), anyChannelPromise()); assertEquals(State.CLOSED, stream.state()); assertTrue(channel.isActive()); } @Test public void receiveRstStream() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 3, request, 31, false); + frameInboundWriter.writeInboundHeaders(3, request, 31, false); Http2Stream stream = frameCodec.connection().stream(3); assertNotNull(stream); @@ -304,7 +302,7 @@ public void receiveRstStream() throws Exception { Http2HeadersFrame actualHeaders = inboundHandler.readInbound(); assertEquals(expectedHeaders.stream(actualHeaders.stream()), actualHeaders); - frameListener.onRstStreamRead(http2HandlerCtx, 3, Http2Error.NO_ERROR.code()); + frameInboundWriter.writeInboundRstStream(3, Http2Error.NO_ERROR.code()); Http2ResetFrame expectedRst = new DefaultHttp2ResetFrame(Http2Error.NO_ERROR).stream(actualHeaders.stream()); Http2ResetFrame actualRst = inboundHandler.readInbound(); @@ -315,8 +313,7 @@ public void receiveRstStream() throws Exception { @Test public void sendGoAway() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 3, request, 31, false); - + frameInboundWriter.writeInboundHeaders(3, request, 31, false); Http2Stream stream = frameCodec.connection().stream(3); assertNotNull(stream); assertEquals(State.OPEN, stream.state()); @@ -324,32 +321,29 @@ public void sendGoAway() throws Exception { ByteBuf debugData = bb("debug"); ByteBuf expected = debugData.copy(); - Http2GoAwayFrame goAwayFrame = new DefaultHttp2GoAwayFrame(Http2Error.NO_ERROR.code(), debugData.slice()); + Http2GoAwayFrame goAwayFrame = new DefaultHttp2GoAwayFrame(Http2Error.NO_ERROR.code(), debugData); goAwayFrame.setExtraStreamIds(2); - inboundHandler.writeOutbound(goAwayFrame); - verify(frameWriter).writeGoAway( - eq(http2HandlerCtx), eq(7), eq(Http2Error.NO_ERROR.code()), eq(expected), anyChannelPromise()); + channel.writeOutbound(goAwayFrame); + verify(frameWriter).writeGoAway(eqFrameCodecCtx(), eq(7), + eq(Http2Error.NO_ERROR.code()), eq(expected), anyChannelPromise()); assertEquals(1, debugData.refCnt()); assertEquals(State.OPEN, stream.state()); assertTrue(channel.isActive()); expected.release(); + debugData.release(); } @Test public void receiveGoaway() throws Exception { ByteBuf debugData = bb("foo"); - frameListener.onGoAwayRead(http2HandlerCtx, 2, Http2Error.NO_ERROR.code(), debugData); - // Release debugData to emulate ByteToMessageDecoder - debugData.release(); + frameInboundWriter.writeInboundGoAway(2, Http2Error.NO_ERROR.code(), debugData); Http2GoAwayFrame expectedFrame = new DefaultHttp2GoAwayFrame(2, Http2Error.NO_ERROR.code(), bb("foo")); Http2GoAwayFrame actualFrame = inboundHandler.readInbound(); - assertEquals(expectedFrame, actualFrame); - assertNull(inboundHandler.readInbound()); + assertEqualsAndRelease(expectedFrame, actualFrame); - expectedFrame.release(); - actualFrame.release(); + assertNull(inboundHandler.readInbound()); } @Test @@ -383,7 +377,7 @@ public ReferenceCounted touch(Object hint) { @Test public void goAwayLastStreamIdOverflowed() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 5, request, 31, false); + frameInboundWriter.writeInboundHeaders(5, request, 31, false); Http2Stream stream = frameCodec.connection().stream(5); assertNotNull(stream); @@ -393,10 +387,10 @@ public void goAwayLastStreamIdOverflowed() throws Exception { Http2GoAwayFrame goAwayFrame = new DefaultHttp2GoAwayFrame(Http2Error.NO_ERROR.code(), debugData.slice()); goAwayFrame.setExtraStreamIds(Integer.MAX_VALUE); - inboundHandler.writeOutbound(goAwayFrame); + channel.writeOutbound(goAwayFrame); // When the last stream id computation overflows, the last stream id should just be set to 2^31 - 1. - verify(frameWriter).writeGoAway(eq(http2HandlerCtx), eq(Integer.MAX_VALUE), eq(Http2Error.NO_ERROR.code()), - eq(debugData), anyChannelPromise()); + verify(frameWriter).writeGoAway(eqFrameCodecCtx(), eq(Integer.MAX_VALUE), + eq(Http2Error.NO_ERROR.code()), eq(debugData), anyChannelPromise()); assertEquals(1, debugData.refCnt()); assertEquals(State.OPEN, stream.state()); assertTrue(channel.isActive()); @@ -404,13 +398,13 @@ public void goAwayLastStreamIdOverflowed() throws Exception { @Test public void streamErrorShouldFireExceptionForInbound() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 3, request, 31, false); + frameInboundWriter.writeInboundHeaders(3, request, 31, false); Http2Stream stream = frameCodec.connection().stream(3); assertNotNull(stream); StreamException streamEx = new StreamException(3, Http2Error.INTERNAL_ERROR, "foo"); - frameCodec.onError(http2HandlerCtx, false, streamEx); + channel.pipeline().fireExceptionCaught(streamEx); Http2FrameStreamEvent event = inboundHandler.readInboundMessageOrUserEvent(); assertEquals(Http2FrameStreamEvent.Type.State, event.type()); @@ -430,13 +424,13 @@ public void streamErrorShouldFireExceptionForInbound() throws Exception { @Test public void streamErrorShouldNotFireExceptionForOutbound() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 3, request, 31, false); + frameInboundWriter.writeInboundHeaders(3, request, 31, false); Http2Stream stream = frameCodec.connection().stream(3); assertNotNull(stream); StreamException streamEx = new StreamException(3, Http2Error.INTERNAL_ERROR, "foo"); - frameCodec.onError(http2HandlerCtx, true, streamEx); + frameCodec.onError(frameCodec.ctx, true, streamEx); Http2FrameStreamEvent event = inboundHandler.readInboundMessageOrUserEvent(); assertEquals(Http2FrameStreamEvent.Type.State, event.type()); @@ -452,14 +446,14 @@ public void streamErrorShouldNotFireExceptionForOutbound() throws Exception { @Test public void windowUpdateFrameDecrementsConsumedBytes() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 3, request, 31, false); + frameInboundWriter.writeInboundHeaders(3, request, 31, false); Http2Connection connection = frameCodec.connection(); Http2Stream stream = connection.stream(3); assertNotNull(stream); ByteBuf data = Unpooled.buffer(100).writeZero(100); - frameListener.onDataRead(http2HandlerCtx, 3, data, 0, true); + frameInboundWriter.writeInboundData(3, data, 0, false); Http2HeadersFrame inboundHeaders = inboundHandler.readInbound(); assertNotNull(inboundHeaders); @@ -472,12 +466,11 @@ public void windowUpdateFrameDecrementsConsumedBytes() throws Exception { int after = connection.local().flowController().unconsumedBytes(stream); assertEquals(100, before - after); assertTrue(f.isSuccess()); - data.release(); } @Test public void windowUpdateMayFail() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 3, request, 31, false); + frameInboundWriter.writeInboundHeaders(3, request, 31, false); Http2Connection connection = frameCodec.connection(); Http2Stream stream = connection.stream(3); assertNotNull(stream); @@ -496,10 +489,10 @@ public void windowUpdateMayFail() throws Exception { @Test public void inboundWindowUpdateShouldBeForwarded() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, 3, request, 31, false); - frameListener.onWindowUpdateRead(http2HandlerCtx, 3, 100); + frameInboundWriter.writeInboundHeaders(3, request, 31, false); + frameInboundWriter.writeInboundWindowUpdate(3, 100); // Connection-level window update - frameListener.onWindowUpdateRead(http2HandlerCtx, 0, 100); + frameInboundWriter.writeInboundWindowUpdate(0, 100); Http2HeadersFrame headersFrame = inboundHandler.readInbound(); assertNotNull(headersFrame); @@ -558,7 +551,7 @@ public void writeUnknownFrame() { unknownFrame.stream(stream); channel.write(unknownFrame); - verify(frameWriter).writeFrame(eq(http2HandlerCtx), eq(unknownFrame.frameType()), + verify(frameWriter).writeFrame(eqFrameCodecCtx(), eq(unknownFrame.frameType()), eq(unknownFrame.stream().id()), eq(unknownFrame.flags()), eq(buffer), any(ChannelPromise.class)); } @@ -567,7 +560,7 @@ public void sendSettingsFrame() { Http2Settings settings = new Http2Settings(); channel.write(new DefaultHttp2SettingsFrame(settings)); - verify(frameWriter).writeSettings(eq(http2HandlerCtx), same(settings), any(ChannelPromise.class)); + verify(frameWriter).writeSettings(eqFrameCodecCtx(), same(settings), any(ChannelPromise.class)); } @Test(timeout = 5000) @@ -619,7 +612,7 @@ public void newOutboundStreamsShouldBeBuffered() throws Exception { assertFalse(promise2.isDone()); // Increase concurrent streams limit to 2 - frameListener.onSettingsRead(http2HandlerCtx, new Http2Settings().maxConcurrentStreams(2)); + frameInboundWriter.writeInboundSettings(new Http2Settings().maxConcurrentStreams(2)); channel.flush(); @@ -643,7 +636,7 @@ public void streamIdentifiersExhausted() throws Http2Exception { @Test public void receivePing() throws Http2Exception { - frameListener.onPingRead(http2HandlerCtx, 12345L); + frameInboundWriter.writeInboundPing(false, 12345L); Http2PingFrame pingFrame = inboundHandler.readInbound(); assertNotNull(pingFrame); @@ -656,13 +649,13 @@ public void receivePing() throws Http2Exception { public void sendPing() { channel.writeAndFlush(new DefaultHttp2PingFrame(12345)); - verify(frameWriter).writePing(eq(http2HandlerCtx), eq(false), eq(12345L), anyChannelPromise()); + verify(frameWriter).writePing(eqFrameCodecCtx(), eq(false), eq(12345L), anyChannelPromise()); } @Test public void receiveSettings() throws Http2Exception { Http2Settings settings = new Http2Settings().maxConcurrentStreams(1); - frameListener.onSettingsRead(http2HandlerCtx, settings); + frameInboundWriter.writeInboundSettings(settings); Http2SettingsFrame settingsFrame = inboundHandler.readInbound(); assertNotNull(settingsFrame); @@ -674,7 +667,7 @@ public void sendSettings() { Http2Settings settings = new Http2Settings().maxConcurrentStreams(1); channel.writeAndFlush(new DefaultHttp2SettingsFrame(settings)); - verify(frameWriter).writeSettings(eq(http2HandlerCtx), eq(settings), anyChannelPromise()); + verify(frameWriter).writeSettings(eqFrameCodecCtx(), eq(settings), anyChannelPromise()); } @Test @@ -682,7 +675,7 @@ public void iterateActiveStreams() throws Exception { setUp(Http2FrameCodecBuilder.forServer().encoderEnforceMaxConcurrentStreams(true), new Http2Settings().maxConcurrentStreams(1)); - frameListener.onHeadersRead(http2HandlerCtx, 3, request, 0, false); + frameInboundWriter.writeInboundHeaders(3, request, 0, false); Http2HeadersFrame headersFrame = inboundHandler.readInbound(); assertNotNull(headersFrame); @@ -736,8 +729,7 @@ public void operationComplete(ChannelFuture future) throws Exception { @Test public void upgradeEventNoRefCntError() throws Exception { - frameListener.onHeadersRead(http2HandlerCtx, Http2CodecUtil.HTTP_UPGRADE_STREAM_ID, request, 31, false); - + frameInboundWriter.writeInboundHeaders(Http2CodecUtil.HTTP_UPGRADE_STREAM_ID, request, 31, false); // Using reflect as the constructor is package-private and the class is final. Constructor constructor = UpgradeEvent.class.getDeclaredConstructor(CharSequence.class, FullHttpRequest.class); @@ -753,7 +745,7 @@ public void upgradeEventNoRefCntError() throws Exception { @Test public void upgradeWithoutFlowControlling() throws Exception { - channel.pipeline().addAfter(http2HandlerCtx.name(), null, new ChannelInboundHandlerAdapter() { + channel.pipeline().addAfter(frameCodec.ctx.name(), null, new ChannelInboundHandlerAdapter() { @Override public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof Http2DataFrame) { @@ -774,7 +766,7 @@ public void operationComplete(ChannelFuture future) throws Exception { } }); - frameListener.onHeadersRead(http2HandlerCtx, Http2CodecUtil.HTTP_UPGRADE_STREAM_ID, request, 31, false); + frameInboundWriter.writeInboundHeaders(Http2CodecUtil.HTTP_UPGRADE_STREAM_ID, request, 31, false); // Using reflect as the constructor is package-private and the class is final. Constructor constructor = @@ -792,24 +784,7 @@ public void operationComplete(ChannelFuture future) throws Exception { channel.pipeline().fireUserEventTriggered(upgradeEvent); } - private static ChannelPromise anyChannelPromise() { - return any(ChannelPromise.class); - } - - private static Http2Settings anyHttp2Settings() { - return any(Http2Settings.class); - } - - private static ByteBuf bb(String s) { - return ByteBufUtil.writeUtf8(UnpooledByteBufAllocator.DEFAULT, s); - } - - private static class VerifiableHttp2FrameWriter extends DefaultHttp2FrameWriter { - @Override - public ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, - int padding, boolean endStream, ChannelPromise promise) { - // duplicate 'data' to prevent readerIndex from being changed, to ease verification - return super.writeData(ctx, streamId, data.duplicate(), padding, endStream, promise); - } + private ChannelHandlerContext eqFrameCodecCtx() { + return eq(frameCodec.ctx); } } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameInboundWriter.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameInboundWriter.java new file mode 100644 index 000000000000..ace50aadbea6 --- /dev/null +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameInboundWriter.java @@ -0,0 +1,340 @@ +/* + * Copyright 2018 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.netty.handler.codec.http2; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelProgressivePromise; +import io.netty.channel.ChannelPromise; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.util.Attribute; +import io.netty.util.AttributeKey; +import io.netty.util.concurrent.EventExecutor; + +import java.net.SocketAddress; + +/** + * Utility class which allows easy writing of HTTP2 frames via {@link EmbeddedChannel#writeInbound(Object...)}. + */ +final class Http2FrameInboundWriter { + + private final ChannelHandlerContext ctx; + private final Http2FrameWriter writer; + + Http2FrameInboundWriter(EmbeddedChannel channel) { + this(channel, new DefaultHttp2FrameWriter()); + } + + Http2FrameInboundWriter(EmbeddedChannel channel, Http2FrameWriter writer) { + this.ctx = new WriteInboundChannelHandlerContext(channel); + this.writer = writer; + } + + void writeInboundData(int streamId, ByteBuf data, int padding, boolean endStream) { + writer.writeData(ctx, streamId, data, padding, endStream, ctx.newPromise()).syncUninterruptibly(); + } + + void writeInboundHeaders(int streamId, Http2Headers headers, + int padding, boolean endStream) { + writer.writeHeaders(ctx, streamId, headers, padding, endStream, ctx.newPromise()).syncUninterruptibly(); + } + + void writeInboundHeaders(int streamId, Http2Headers headers, + int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) { + writer.writeHeaders(ctx, streamId, headers, streamDependency, + weight, exclusive, padding, endStream, ctx.newPromise()).syncUninterruptibly(); + } + + void writeInboundPriority(int streamId, int streamDependency, + short weight, boolean exclusive) { + writer.writePriority(ctx, streamId, streamDependency, weight, + exclusive, ctx.newPromise()).syncUninterruptibly(); + } + + void writeInboundRstStream(int streamId, long errorCode) { + writer.writeRstStream(ctx, streamId, errorCode, ctx.newPromise()).syncUninterruptibly(); + } + + void writeInboundSettings(Http2Settings settings) { + writer.writeSettings(ctx, settings, ctx.newPromise()).syncUninterruptibly(); + } + + void writeInboundSettingsAck() { + writer.writeSettingsAck(ctx, ctx.newPromise()).syncUninterruptibly(); + } + + void writeInboundPing(boolean ack, long data) { + writer.writePing(ctx, ack, data, ctx.newPromise()).syncUninterruptibly(); + } + + void writePushPromise(int streamId, int promisedStreamId, + Http2Headers headers, int padding) { + writer.writePushPromise(ctx, streamId, promisedStreamId, + headers, padding, ctx.newPromise()).syncUninterruptibly(); + } + + void writeInboundGoAway(int lastStreamId, long errorCode, ByteBuf debugData) { + writer.writeGoAway(ctx, lastStreamId, errorCode, debugData, ctx.newPromise()).syncUninterruptibly(); + } + + void writeInboundWindowUpdate(int streamId, int windowSizeIncrement) { + writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement, ctx.newPromise()).syncUninterruptibly(); + } + + void writeInboundFrame(byte frameType, int streamId, + Http2Flags flags, ByteBuf payload) { + writer.writeFrame(ctx, frameType, streamId, flags, payload, ctx.newPromise()).syncUninterruptibly(); + } + + private static final class WriteInboundChannelHandlerContext extends ChannelOutboundHandlerAdapter + implements ChannelHandlerContext { + private final EmbeddedChannel channel; + + WriteInboundChannelHandlerContext(EmbeddedChannel channel) { + this.channel = channel; + } + + @Override + public Channel channel() { + return channel; + } + + @Override + public EventExecutor executor() { + return channel.eventLoop(); + } + + @Override + public String name() { + return "WriteInbound"; + } + + @Override + public ChannelHandler handler() { + return this; + } + + @Override + public boolean isRemoved() { + return false; + } + + @Override + public ChannelHandlerContext fireChannelRegistered() { + channel.pipeline().fireChannelRegistered(); + return this; + } + + @Override + public ChannelHandlerContext fireChannelUnregistered() { + channel.pipeline().fireChannelUnregistered(); + return this; + } + + @Override + public ChannelHandlerContext fireChannelActive() { + channel.pipeline().fireChannelActive(); + return this; + } + + @Override + public ChannelHandlerContext fireChannelInactive() { + channel.pipeline().fireChannelInactive(); + return this; + } + + @Override + public ChannelHandlerContext fireExceptionCaught(Throwable cause) { + channel.pipeline().fireExceptionCaught(cause); + return this; + } + + @Override + public ChannelHandlerContext fireUserEventTriggered(Object evt) { + channel.pipeline().fireUserEventTriggered(evt); + return this; + } + + @Override + public ChannelHandlerContext fireChannelRead(Object msg) { + channel.pipeline().fireChannelRead(msg); + return this; + } + + @Override + public ChannelHandlerContext fireChannelReadComplete() { + channel.pipeline().fireChannelReadComplete(); + return this; + } + + @Override + public ChannelHandlerContext fireChannelWritabilityChanged() { + channel.pipeline().fireChannelWritabilityChanged(); + return this; + } + + @Override + public ChannelHandlerContext read() { + channel.read(); + return this; + } + + @Override + public ChannelHandlerContext flush() { + channel.pipeline().fireChannelReadComplete(); + return this; + } + + @Override + public ChannelPipeline pipeline() { + return channel.pipeline(); + } + + @Override + public ByteBufAllocator alloc() { + return channel.alloc(); + } + + @Override + public Attribute attr(AttributeKey key) { + return channel.attr(key); + } + + @Override + public boolean hasAttr(AttributeKey key) { + return channel.hasAttr(key); + } + + @Override + public ChannelFuture bind(SocketAddress localAddress) { + return channel.bind(localAddress); + } + + @Override + public ChannelFuture connect(SocketAddress remoteAddress) { + return channel.connect(remoteAddress); + } + + @Override + public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) { + return channel.connect(remoteAddress, localAddress); + } + + @Override + public ChannelFuture disconnect() { + return channel.disconnect(); + } + + @Override + public ChannelFuture close() { + return channel.close(); + } + + @Override + public ChannelFuture deregister() { + return channel.deregister(); + } + + @Override + public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { + return channel.bind(localAddress, promise); + } + + @Override + public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { + return channel.connect(remoteAddress, promise); + } + + @Override + public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { + return channel.connect(remoteAddress, localAddress, promise); + } + + @Override + public ChannelFuture disconnect(ChannelPromise promise) { + return channel.disconnect(promise); + } + + @Override + public ChannelFuture close(ChannelPromise promise) { + return channel.close(promise); + } + + @Override + public ChannelFuture deregister(ChannelPromise promise) { + return channel.deregister(promise); + } + + @Override + public ChannelFuture write(Object msg) { + return write(msg, newPromise()); + } + + @Override + public ChannelFuture write(Object msg, ChannelPromise promise) { + return writeAndFlush(msg, promise); + } + + @Override + public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { + try { + channel.writeInbound(msg); + channel.runPendingTasks(); + promise.setSuccess(); + } catch (Throwable cause) { + promise.setFailure(cause); + } + return promise; + } + + @Override + public ChannelFuture writeAndFlush(Object msg) { + return writeAndFlush(msg, newPromise()); + } + + @Override + public ChannelPromise newPromise() { + return channel.newPromise(); + } + + @Override + public ChannelProgressivePromise newProgressivePromise() { + return channel.newProgressivePromise(); + } + + @Override + public ChannelFuture newSucceededFuture() { + return channel.newSucceededFuture(); + } + + @Override + public ChannelFuture newFailedFuture(Throwable cause) { + return channel.newFailedFuture(cause); + } + + @Override + public ChannelPromise voidPromise() { + return channel.voidPromise(); + } + } +} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java index d857cf8d1e6f..7788e6dd64c7 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java @@ -15,8 +15,7 @@ package io.netty.handler.codec.http2; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -31,11 +30,13 @@ import io.netty.handler.codec.http2.LastInboundHandler.Consumer; import io.netty.util.AsciiString; import io.netty.util.AttributeKey; -import io.netty.util.ReferenceCountUtil; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.net.InetSocketAddress; import java.nio.channels.ClosedChannelException; @@ -43,34 +44,43 @@ import java.util.Queue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import static io.netty.util.ReferenceCountUtil.release; -import static org.hamcrest.Matchers.instanceOf; +import static io.netty.handler.codec.http2.Http2TestUtil.anyChannelPromise; +import static io.netty.handler.codec.http2.Http2TestUtil.anyHttp2Settings; +import static io.netty.handler.codec.http2.Http2TestUtil.assertEqualsAndRelease; +import static io.netty.handler.codec.http2.Http2TestUtil.bb; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyShort; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * Unit tests for {@link Http2MultiplexCodec}. */ public class Http2MultiplexCodecTest { - - private EmbeddedChannel parentChannel; - private Writer writer; - - private TestChannelInitializer childChannelInitializer; - - private static final Http2Headers request = new DefaultHttp2Headers() + private final Http2Headers request = new DefaultHttp2Headers() .method(HttpMethod.GET.asciiName()).scheme(HttpScheme.HTTPS.name()) .authority(new AsciiString("example.org")).path(new AsciiString("/foo")); - private TestableHttp2MultiplexCodec codec; - private TestableHttp2MultiplexCodec.Stream inboundStream; - private TestableHttp2MultiplexCodec.Stream outboundStream; + private EmbeddedChannel parentChannel; + private Http2FrameWriter frameWriter; + private Http2FrameInboundWriter frameInboundWriter; + private TestChannelInitializer childChannelInitializer; + private Http2MultiplexCodec codec; private static final int initialRemoteStreamWindow = 1024; @@ -78,25 +88,38 @@ public class Http2MultiplexCodecTest { public void setUp() { childChannelInitializer = new TestChannelInitializer(); parentChannel = new EmbeddedChannel(); - writer = new Writer(); - + frameInboundWriter = new Http2FrameInboundWriter(parentChannel); parentChannel.connect(new InetSocketAddress(0)); - codec = new TestableHttp2MultiplexCodecBuilder(true, childChannelInitializer).build(); + frameWriter = Http2TestUtil.mockedFrameWriter(); + codec = new Http2MultiplexCodecBuilder(true, childChannelInitializer).frameWriter(frameWriter).build(); parentChannel.pipeline().addLast(codec); parentChannel.runPendingTasks(); + parentChannel.pipeline().fireChannelActive(); + + parentChannel.writeInbound(Http2CodecUtil.connectionPrefaceBuf()); Http2Settings settings = new Http2Settings().initialWindowSize(initialRemoteStreamWindow); - codec.onHttp2Frame(new DefaultHttp2SettingsFrame(settings)); + frameInboundWriter.writeInboundSettings(settings); + + verify(frameWriter).writeSettingsAck(eqMultiplexCodecCtx(), anyChannelPromise()); + + frameInboundWriter.writeInboundSettingsAck(); + + Http2SettingsFrame settingsFrame = parentChannel.readInbound(); + assertNotNull(settingsFrame); + + // Handshake + verify(frameWriter).writeSettings(eqMultiplexCodecCtx(), + anyHttp2Settings(), anyChannelPromise()); + } - inboundStream = codec.newStream(); - inboundStream.id = 3; - outboundStream = codec.newStream(); - outboundStream.id = 2; + private ChannelHandlerContext eqMultiplexCodecCtx() { + return eq(codec.ctx); } @After public void tearDown() throws Exception { - if (childChannelInitializer.handler != null) { + if (childChannelInitializer.handler instanceof LastInboundHandler) { ((LastInboundHandler) childChannelInitializer.handler).finishAndReleaseAll(); } parentChannel.finishAndReleaseAll(); @@ -110,154 +133,165 @@ public void tearDown() throws Exception { @Test public void writeUnknownFrame() { - childChannelInitializer.handler = new ChannelInboundHandlerAdapter() { + Http2StreamChannel childChannel = newOutboundStream(new ChannelInboundHandlerAdapter() { @Override public void channelActive(ChannelHandlerContext ctx) { ctx.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); ctx.writeAndFlush(new DefaultHttp2UnknownFrame((byte) 99, new Http2Flags())); ctx.fireChannelActive(); } - }; - - Channel childChannel = newOutboundStream(); + }); assertTrue(childChannel.isActive()); - Http2FrameStream stream = readOutboundHeadersAndAssignId(); parentChannel.runPendingTasks(); - Http2UnknownFrame frame = parentChannel.readOutbound(); - assertEquals(stream, frame.stream()); - assertEquals(99, frame.frameType()); - assertEquals(new Http2Flags(), frame.flags()); - frame.release(); + verify(frameWriter).writeFrame(eq(codec.ctx), eq((byte) 99), eqStreamId(childChannel), any(Http2Flags.class), + any(ByteBuf.class), any(ChannelPromise.class)); + } + + private Http2StreamChannel newInboundStream(int streamId, boolean endStream, final ChannelHandler childHandler) { + return newInboundStream(streamId, endStream, null, childHandler); + } + + private Http2StreamChannel newInboundStream(int streamId, boolean endStream, + AtomicInteger maxReads, final ChannelHandler childHandler) { + final AtomicReference streamChannelRef = new AtomicReference(); + childChannelInitializer.maxReads = maxReads; + childChannelInitializer.handler = new ChannelInboundHandlerAdapter() { + @Override + public void channelRegistered(ChannelHandlerContext ctx) { + assertNull(streamChannelRef.get()); + streamChannelRef.set((Http2StreamChannel) ctx.channel()); + ctx.pipeline().addLast(childHandler); + ctx.fireChannelRegistered(); + } + }; + + frameInboundWriter.writeInboundHeaders(streamId, request, 0, endStream); + parentChannel.runPendingTasks(); + Http2StreamChannel channel = streamChannelRef.get(); + assertEquals(streamId, channel.stream().id()); + return channel; } @Test public void readUnkownFrame() { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - codec.onHttp2Frame(new DefaultHttp2UnknownFrame((byte) 99, new Http2Flags()).stream(inboundStream)); - codec.onChannelReadComplete(); + LastInboundHandler handler = new LastInboundHandler(); + + Http2StreamChannel channel = newInboundStream(3, true, handler); + frameInboundWriter.writeInboundFrame((byte) 99, channel.stream().id(), new Http2Flags(), Unpooled.EMPTY_BUFFER); - // headers and unknown frame - verifyFramesMultiplexedToCorrectChannel(inboundStream, inboundHandler, 2); + // header frame and unknown frame + verifyFramesMultiplexedToCorrectChannel(channel, handler, 2); - Channel childChannel = newOutboundStream(); + Channel childChannel = newOutboundStream(new ChannelInboundHandlerAdapter()); assertTrue(childChannel.isActive()); } @Test public void headerAndDataFramesShouldBeDelivered() { LastInboundHandler inboundHandler = new LastInboundHandler(); - childChannelInitializer.handler = inboundHandler; - Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(request).stream(inboundStream); - Http2DataFrame dataFrame1 = new DefaultHttp2DataFrame(bb("hello")).stream(inboundStream); - Http2DataFrame dataFrame2 = new DefaultHttp2DataFrame(bb("world")).stream(inboundStream); + Http2StreamChannel channel = newInboundStream(3, false, inboundHandler); + Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(request).stream(channel.stream()); + Http2DataFrame dataFrame1 = new DefaultHttp2DataFrame(bb("hello")).stream(channel.stream()); + Http2DataFrame dataFrame2 = new DefaultHttp2DataFrame(bb("world")).stream(channel.stream()); - assertFalse(inboundHandler.isChannelActive()); - inboundStream.state = Http2Stream.State.OPEN; - codec.onHttp2StreamStateChanged(inboundStream); - codec.onHttp2Frame(headersFrame); assertTrue(inboundHandler.isChannelActive()); - codec.onHttp2Frame(dataFrame1); - codec.onHttp2Frame(dataFrame2); + frameInboundWriter.writeInboundData(channel.stream().id(), bb("hello"), 0, false); + frameInboundWriter.writeInboundData(channel.stream().id(), bb("world"), 0, false); assertEquals(headersFrame, inboundHandler.readInbound()); - assertEquals(dataFrame1, inboundHandler.readInbound()); - assertEquals(dataFrame2, inboundHandler.readInbound()); - assertNull(inboundHandler.readInbound()); - dataFrame1.release(); - dataFrame2.release(); + assertEqualsAndRelease(dataFrame1, inboundHandler.readInbound()); + assertEqualsAndRelease(dataFrame2, inboundHandler.readInbound()); + + assertNull(inboundHandler.readInbound()); } @Test public void framesShouldBeMultiplexed() { + LastInboundHandler handler1 = new LastInboundHandler(); + Http2StreamChannel channel1 = newInboundStream(3, false, handler1); + LastInboundHandler handler2 = new LastInboundHandler(); + Http2StreamChannel channel2 = newInboundStream(5, false, handler2); + LastInboundHandler handler3 = new LastInboundHandler(); + Http2StreamChannel channel3 = newInboundStream(11, false, handler3); + + verifyFramesMultiplexedToCorrectChannel(channel1, handler1, 1); + verifyFramesMultiplexedToCorrectChannel(channel2, handler2, 1); + verifyFramesMultiplexedToCorrectChannel(channel3, handler3, 1); + + frameInboundWriter.writeInboundData(channel2.stream().id(), bb("hello"), 0, false); + frameInboundWriter.writeInboundData(channel1.stream().id(), bb("foo"), 0, true); + frameInboundWriter.writeInboundData(channel2.stream().id(), bb("world"), 0, true); + frameInboundWriter.writeInboundData(channel3.stream().id(), bb("bar"), 0, true); - TestableHttp2MultiplexCodec.Stream stream3 = codec.newStream(); - stream3.id = 3; - TestableHttp2MultiplexCodec.Stream stream5 = codec.newStream(); - stream5.id = 5; - - TestableHttp2MultiplexCodec.Stream stream11 = codec.newStream(); - stream11.id = 11; - - LastInboundHandler inboundHandler3 = streamActiveAndWriteHeaders(stream3); - LastInboundHandler inboundHandler5 = streamActiveAndWriteHeaders(stream5); - LastInboundHandler inboundHandler11 = streamActiveAndWriteHeaders(stream11); - - verifyFramesMultiplexedToCorrectChannel(stream3, inboundHandler3, 1); - verifyFramesMultiplexedToCorrectChannel(stream5, inboundHandler5, 1); - verifyFramesMultiplexedToCorrectChannel(stream11, inboundHandler11, 1); - - codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("hello"), false).stream(stream5)); - codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("foo"), true).stream(stream3)); - codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("world"), true).stream(stream5)); - codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("bar"), true).stream(stream11)); - verifyFramesMultiplexedToCorrectChannel(stream5, inboundHandler5, 2); - verifyFramesMultiplexedToCorrectChannel(stream3, inboundHandler3, 1); - verifyFramesMultiplexedToCorrectChannel(stream11, inboundHandler11, 1); + verifyFramesMultiplexedToCorrectChannel(channel1, handler1, 1); + verifyFramesMultiplexedToCorrectChannel(channel2, handler2, 2); + verifyFramesMultiplexedToCorrectChannel(channel3, handler3, 1); } @Test - public void inboundDataFrameShouldEmitWindowUpdateFrame() { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); + public void inboundDataFrameShouldUpdateLocalFlowController() throws Http2Exception { + Http2LocalFlowController flowController = Mockito.mock(Http2LocalFlowController.class); + codec.connection().local().flowController(flowController); + + LastInboundHandler handler = new LastInboundHandler(); + final Http2StreamChannel channel = newInboundStream(3, false, handler); + ByteBuf tenBytes = bb("0123456789"); - codec.onHttp2Frame(new DefaultHttp2DataFrame(tenBytes, true).stream(inboundStream)); - codec.onChannelReadComplete(); - Http2WindowUpdateFrame windowUpdate = parentChannel.readOutbound(); - assertNotNull(windowUpdate); + frameInboundWriter.writeInboundData(channel.stream().id(), tenBytes, 0, true); - assertEquals(inboundStream, windowUpdate.stream()); - assertEquals(10, windowUpdate.windowSizeIncrement()); + // Verify we marked the bytes as consumed + verify(flowController).consumeBytes(argThat(new ArgumentMatcher() { + @Override + public boolean matches(Http2Stream http2Stream) { + return http2Stream.id() == channel.stream().id(); + } + }), eq(10)); // headers and data frame - verifyFramesMultiplexedToCorrectChannel(inboundStream, inboundHandler, 2); + verifyFramesMultiplexedToCorrectChannel(channel, handler, 2); } @Test public void unhandledHttp2FramesShouldBePropagated() { - assertThat(parentChannel.readInbound(), instanceOf(Http2SettingsFrame.class)); - Http2PingFrame pingFrame = new DefaultHttp2PingFrame(0); - codec.onHttp2Frame(pingFrame); - assertSame(parentChannel.readInbound(), pingFrame); + frameInboundWriter.writeInboundPing(false, 0); + assertEquals(parentChannel.readInbound(), pingFrame); - DefaultHttp2GoAwayFrame goAwayFrame = - new DefaultHttp2GoAwayFrame(1, parentChannel.alloc().buffer().writeLong(8)); - codec.onHttp2Frame(goAwayFrame); + DefaultHttp2GoAwayFrame goAwayFrame = new DefaultHttp2GoAwayFrame(1, + parentChannel.alloc().buffer().writeLong(8)); + frameInboundWriter.writeInboundGoAway(0, goAwayFrame.errorCode(), goAwayFrame.content().retainedDuplicate()); Http2GoAwayFrame frame = parentChannel.readInbound(); - assertSame(frame, goAwayFrame); - assertTrue(frame.release()); + assertEqualsAndRelease(frame, goAwayFrame); } @Test public void channelReadShouldRespectAutoRead() { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - Channel childChannel = inboundHandler.channel(); + LastInboundHandler inboundHandler = new LastInboundHandler(); + Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); assertTrue(childChannel.config().isAutoRead()); Http2HeadersFrame headersFrame = inboundHandler.readInbound(); assertNotNull(headersFrame); childChannel.config().setAutoRead(false); - codec.onHttp2Frame( - new DefaultHttp2DataFrame(bb("hello world"), false).stream(inboundStream)); - codec.onChannelReadComplete(); + + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("hello world"), 0, false); Http2DataFrame dataFrame0 = inboundHandler.readInbound(); assertNotNull(dataFrame0); release(dataFrame0); - codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("foo"), false).stream(inboundStream)); - codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("bar"), true).stream(inboundStream)); - codec.onChannelReadComplete(); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("foo"), 0, false); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("bar"), 0, false); - dataFrame0 = inboundHandler.readInbound(); - assertNull(dataFrame0); + assertNull(inboundHandler.readInbound()); childChannel.config().setAutoRead(true); - verifyFramesMultiplexedToCorrectChannel(inboundStream, inboundHandler, 2); + verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 2); } @Test @@ -271,8 +305,8 @@ public void readInChannelReadCompleteWithoutAutoRead() { } private void useReadWithoutAutoRead(final boolean readComplete) { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - Channel childChannel = inboundHandler.channel(); + LastInboundHandler inboundHandler = new LastInboundHandler(); + Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); assertTrue(childChannel.config().isAutoRead()); childChannel.config().setAutoRead(false); assertFalse(childChannel.config().isAutoRead()); @@ -299,23 +333,18 @@ public void channelReadComplete(ChannelHandlerContext ctx) { } }); - codec.onHttp2Frame( - new DefaultHttp2DataFrame(bb("hello world"), false).stream(inboundStream)); - codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("foo"), false).stream(inboundStream)); - codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("bar"), true).stream(inboundStream)); - codec.onChannelReadComplete(); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("hello world"), 0, false); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("foo"), 0, false); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("bar"), 0, false); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("hello world"), 0, false); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("foo"), 0, false); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("bar"), 0, true); - codec.onHttp2Frame( - new DefaultHttp2DataFrame(bb("hello world"), false).stream(inboundStream)); - codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("foo"), false).stream(inboundStream)); - codec.onHttp2Frame(new DefaultHttp2DataFrame(bb("bar"), true).stream(inboundStream)); - codec.onChannelReadComplete(); - - verifyFramesMultiplexedToCorrectChannel(inboundStream, inboundHandler, 6); + verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 6); } - private Http2StreamChannel newOutboundStream() { - return new Http2StreamChannelBootstrap(parentChannel).handler(childChannelInitializer) + private Http2StreamChannel newOutboundStream(ChannelHandler handler) { + return new Http2StreamChannelBootstrap(parentChannel).handler(handler) .open().syncUninterruptibly().getNow(); } @@ -323,12 +352,11 @@ private Http2StreamChannel newOutboundStream() { * A child channel for a HTTP/2 stream in IDLE state (that is no headers sent or received), * should not emit a RST_STREAM frame on close, as this is a connection error of type protocol error. */ - @Test public void idleOutboundStreamShouldNotWriteResetFrameOnClose() { - childChannelInitializer.handler = new LastInboundHandler(); + LastInboundHandler handler = new LastInboundHandler(); - Channel childChannel = newOutboundStream(); + Channel childChannel = newOutboundStream(handler); assertTrue(childChannel.isActive()); childChannel.close(); @@ -341,124 +369,105 @@ public void idleOutboundStreamShouldNotWriteResetFrameOnClose() { @Test public void outboundStreamShouldWriteResetFrameOnClose_headersSent() { - childChannelInitializer.handler = new ChannelInboundHandlerAdapter() { + ChannelHandler handler = new ChannelInboundHandlerAdapter() { @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { + public void channelActive(ChannelHandlerContext ctx) { ctx.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); ctx.fireChannelActive(); } }; - Channel childChannel = newOutboundStream(); + Http2StreamChannel childChannel = newOutboundStream(handler); assertTrue(childChannel.isActive()); - Http2FrameStream stream2 = readOutboundHeadersAndAssignId(); - childChannel.close(); - parentChannel.runPendingTasks(); - - Http2ResetFrame reset = parentChannel.readOutbound(); - assertEquals(stream2, reset.stream()); - assertEquals(Http2Error.CANCEL.code(), reset.errorCode()); + verify(frameWriter).writeRstStream(eqMultiplexCodecCtx(), + eqStreamId(childChannel), eq(Http2Error.CANCEL.code()), anyChannelPromise()); } @Test public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() { - writer = new Writer() { + when(frameWriter.writeHeaders(eqMultiplexCodecCtx(), anyInt(), + any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), + any(ChannelPromise.class))).thenAnswer(new Answer() { + private boolean headersWritten; @Override - void write(Object msg, ChannelPromise promise) { + public ChannelFuture answer(InvocationOnMock invocationOnMock) { // 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. - if (!headersWritten && msg instanceof Http2HeadersFrame) { + if (!headersWritten) { headersWritten = true; - Http2HeadersFrame headersFrame = (Http2HeadersFrame) msg; - final TestableHttp2MultiplexCodec.Stream stream = - (TestableHttp2MultiplexCodec.Stream) headersFrame.stream(); - stream.id = 1; - promise.setFailure(new Exception("boom")); - } else { - super.write(msg, promise); + return ((ChannelPromise) invocationOnMock.getArgument(8)).setFailure(new Exception("boom")); } + return ((ChannelPromise) invocationOnMock.getArgument(8)).setSuccess(); } - }; + }); - childChannelInitializer.handler = new ChannelInboundHandlerAdapter() { + Http2StreamChannel childChannel = newOutboundStream(new ChannelInboundHandlerAdapter() { @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { + public void channelActive(ChannelHandlerContext ctx) { ctx.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); ctx.fireChannelActive(); } - }; + }); - Channel childChannel = newOutboundStream(); assertFalse(childChannel.isActive()); childChannel.close(); parentChannel.runPendingTasks(); + // The channel was never active so we should not generate a RST frame. + verify(frameWriter, never()).writeRstStream(eqMultiplexCodecCtx(), eqStreamId(childChannel), anyLong(), + anyChannelPromise()); + assertTrue(parentChannel.outboundMessages().isEmpty()); } @Test public void inboundRstStreamFireChannelInactive() { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); + LastInboundHandler inboundHandler = new LastInboundHandler(); + Http2StreamChannel channel = newInboundStream(3, false, inboundHandler); assertTrue(inboundHandler.isChannelActive()); - codec.onHttp2Frame(new DefaultHttp2ResetFrame(Http2Error.INTERNAL_ERROR) - .stream(inboundStream)); - codec.onChannelReadComplete(); - - // This will be called by the frame codec. - inboundStream.state = Http2Stream.State.CLOSED; - codec.onHttp2StreamStateChanged(inboundStream); - parentChannel.runPendingTasks(); + frameInboundWriter.writeInboundRstStream(channel.stream().id(), Http2Error.INTERNAL_ERROR.code()); assertFalse(inboundHandler.isChannelActive()); + // A RST_STREAM frame should NOT be emitted, as we received a RST_STREAM. - assertNull(parentChannel.readOutbound()); + verify(frameWriter, Mockito.never()).writeRstStream(eqMultiplexCodecCtx(), eqStreamId(channel), + anyLong(), anyChannelPromise()); } @Test(expected = StreamException.class) public void streamExceptionTriggersChildChannelExceptionAndClose() throws Exception { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - - StreamException cause = new StreamException(inboundStream.id(), Http2Error.PROTOCOL_ERROR, "baaam!"); - Http2FrameStreamException http2Ex = new Http2FrameStreamException( - inboundStream, Http2Error.PROTOCOL_ERROR, cause); - codec.onHttp2FrameStreamException(http2Ex); - - inboundHandler.checkException(); - } - - @Test(expected = StreamException.class) - public void streamExceptionClosesChildChannel() throws Exception { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - - assertTrue(inboundHandler.isChannelActive()); - StreamException cause = new StreamException(inboundStream.id(), Http2Error.PROTOCOL_ERROR, "baaam!"); - Http2FrameStreamException http2Ex = new Http2FrameStreamException( - inboundStream, Http2Error.PROTOCOL_ERROR, cause); - codec.onHttp2FrameStreamException(http2Ex); - parentChannel.runPendingTasks(); + LastInboundHandler inboundHandler = new LastInboundHandler(); + Http2StreamChannel channel = newInboundStream(3, false, inboundHandler); + assertTrue(channel.isActive()); + StreamException cause = new StreamException(channel.stream().id(), Http2Error.PROTOCOL_ERROR, "baaam!"); + parentChannel.pipeline().fireExceptionCaught(cause); - assertFalse(inboundHandler.isChannelActive()); + assertFalse(channel.isActive()); inboundHandler.checkException(); } @Test(expected = ClosedChannelException.class) public void streamClosedErrorTranslatedToClosedChannelExceptionOnWrites() throws Exception { - writer = new Writer() { - @Override - void write(Object msg, ChannelPromise promise) { - promise.tryFailure(new StreamException(inboundStream.id(), Http2Error.STREAM_CLOSED, "Stream Closed")); - } - }; LastInboundHandler inboundHandler = new LastInboundHandler(); - childChannelInitializer.handler = inboundHandler; - Channel childChannel = newOutboundStream(); + final Http2StreamChannel childChannel = newOutboundStream(inboundHandler); assertTrue(childChannel.isActive()); + Http2Headers headers = new DefaultHttp2Headers(); + when(frameWriter.writeHeaders(eqMultiplexCodecCtx(), anyInt(), + eq(headers), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), + any(ChannelPromise.class))).thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + return ((ChannelPromise) invocationOnMock.getArgument(8)).setFailure( + new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed")); + } + }); ChannelFuture future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); + parentChannel.flush(); assertFalse(childChannel.isActive()); @@ -472,9 +481,7 @@ void write(Object msg, ChannelPromise promise) { @Test public void creatingWritingReadingAndClosingOutboundStreamShouldWork() { LastInboundHandler inboundHandler = new LastInboundHandler(); - childChannelInitializer.handler = inboundHandler; - - Http2StreamChannel childChannel = newOutboundStream(); + Http2StreamChannel childChannel = newOutboundStream(inboundHandler); assertTrue(childChannel.isActive()); assertTrue(inboundHandler.isChannelActive()); @@ -482,25 +489,21 @@ public void creatingWritingReadingAndClosingOutboundStreamShouldWork() { Http2Headers headers = new DefaultHttp2Headers().scheme("https").method("GET").path("/foo.txt"); childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers)); - readOutboundHeadersAndAssignId(); - // Read from the child channel - headers = new DefaultHttp2Headers().scheme("https").status("200"); - codec.onHttp2Frame(new DefaultHttp2HeadersFrame(headers).stream(childChannel.stream())); - codec.onChannelReadComplete(); + frameInboundWriter.writeInboundHeaders(childChannel.stream().id(), headers, 0, false); Http2HeadersFrame headersFrame = inboundHandler.readInbound(); assertNotNull(headersFrame); - assertSame(headers, headersFrame.headers()); + assertEquals(headers, headersFrame.headers()); // Close the child channel. childChannel.close(); parentChannel.runPendingTasks(); // An active outbound stream should emit a RST_STREAM frame. - Http2ResetFrame rstFrame = parentChannel.readOutbound(); - assertNotNull(rstFrame); - assertEquals(childChannel.stream(), rstFrame.stream()); + verify(frameWriter).writeRstStream(eqMultiplexCodecCtx(), eqStreamId(childChannel), + anyLong(), anyChannelPromise()); + assertFalse(childChannel.isOpen()); assertFalse(childChannel.isActive()); assertFalse(inboundHandler.isChannelActive()); @@ -511,33 +514,36 @@ public void creatingWritingReadingAndClosingOutboundStreamShouldWork() { // @Test(expected = Http2NoMoreStreamIdsException.class) public void failedOutboundStreamCreationThrowsAndClosesChannel() throws Exception { - writer = new Writer() { - @Override - void write(Object msg, ChannelPromise promise) { - promise.tryFailure(new Http2NoMoreStreamIdsException()); - } - }; - LastInboundHandler inboundHandler = new LastInboundHandler(); - childChannelInitializer.handler = inboundHandler; - - Channel childChannel = newOutboundStream(); + LastInboundHandler handler = new LastInboundHandler(); + Http2StreamChannel childChannel = newOutboundStream(handler); assertTrue(childChannel.isActive()); - ChannelFuture future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); + Http2Headers headers = new DefaultHttp2Headers(); + when(frameWriter.writeHeaders(eqMultiplexCodecCtx(), anyInt(), + eq(headers), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), + any(ChannelPromise.class))).thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + return ((ChannelPromise) invocationOnMock.getArgument(8)).setFailure( + new Http2NoMoreStreamIdsException()); + } + }); + + ChannelFuture future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers)); parentChannel.flush(); assertFalse(childChannel.isActive()); assertFalse(childChannel.isOpen()); - inboundHandler.checkException(); + handler.checkException(); future.syncUninterruptibly(); } @Test public void channelClosedWhenCloseListenerCompletes() { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + LastInboundHandler inboundHandler = new LastInboundHandler(); + Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); assertTrue(childChannel.isOpen()); assertTrue(childChannel.isActive()); @@ -564,42 +570,35 @@ public void operationComplete(ChannelFuture future) { @Test public void channelClosedWhenChannelClosePromiseCompletes() { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + LastInboundHandler inboundHandler = new LastInboundHandler(); + Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - assertTrue(childChannel.isOpen()); - assertTrue(childChannel.isActive()); + assertTrue(childChannel.isOpen()); + assertTrue(childChannel.isActive()); - final AtomicBoolean channelOpen = new AtomicBoolean(true); - final AtomicBoolean channelActive = new AtomicBoolean(true); + final AtomicBoolean channelOpen = new AtomicBoolean(true); + final AtomicBoolean channelActive = new AtomicBoolean(true); - childChannel.closeFuture().addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) { - channelOpen.set(future.channel().isOpen()); - channelActive.set(future.channel().isActive()); - } - }); - childChannel.close().syncUninterruptibly(); + childChannel.closeFuture().addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) { + channelOpen.set(future.channel().isOpen()); + channelActive.set(future.channel().isActive()); + } + }); + childChannel.close().syncUninterruptibly(); - assertFalse(channelOpen.get()); - assertFalse(channelActive.get()); - assertFalse(childChannel.isActive()); + assertFalse(channelOpen.get()); + assertFalse(channelActive.get()); + assertFalse(childChannel.isActive()); } @Test public void channelClosedWhenWriteFutureFails() { final Queue writePromises = new ArrayDeque(); - writer = new Writer() { - @Override - void write(Object msg, ChannelPromise promise) { - ReferenceCountUtil.release(msg); - writePromises.offer(promise); - } - }; - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + LastInboundHandler inboundHandler = new LastInboundHandler(); + Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); assertTrue(childChannel.isOpen()); assertTrue(childChannel.isActive()); @@ -607,7 +606,19 @@ void write(Object msg, ChannelPromise promise) { final AtomicBoolean channelOpen = new AtomicBoolean(true); final AtomicBoolean channelActive = new AtomicBoolean(true); - ChannelFuture f = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); + Http2Headers headers = new DefaultHttp2Headers(); + when(frameWriter.writeHeaders(eqMultiplexCodecCtx(), anyInt(), + eq(headers), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), + any(ChannelPromise.class))).thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + ChannelPromise promise = invocationOnMock.getArgument(8); + writePromises.offer(promise); + return promise; + } + }); + + ChannelFuture f = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers)); assertFalse(f.isDone()); f.addListener(new ChannelFutureListener() { @Override @@ -628,8 +639,8 @@ public void operationComplete(ChannelFuture future) throws Exception { @Test public void channelClosedTwiceMarksPromiseAsSuccessful() { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + LastInboundHandler inboundHandler = new LastInboundHandler(); + Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); assertTrue(childChannel.isOpen()); assertTrue(childChannel.isActive()); @@ -644,7 +655,7 @@ public void channelClosedTwiceMarksPromiseAsSuccessful() { public void settingChannelOptsAndAttrs() { AttributeKey key = AttributeKey.newInstance("foo"); - Channel childChannel = newOutboundStream(); + Channel childChannel = newOutboundStream(new ChannelInboundHandlerAdapter()); childChannel.config().setAutoRead(false).setWriteSpinCount(1000); childChannel.attr(key).set("bar"); assertFalse(childChannel.config().isAutoRead()); @@ -654,50 +665,52 @@ public void settingChannelOptsAndAttrs() { @Test public void outboundFlowControlWritability() { - Http2StreamChannel childChannel = newOutboundStream(); + Http2StreamChannel childChannel = newOutboundStream(new ChannelInboundHandlerAdapter()); assertTrue(childChannel.isActive()); assertTrue(childChannel.isWritable()); childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); parentChannel.flush(); - Http2FrameStream stream = readOutboundHeadersAndAssignId(); - // Test for initial window size assertEquals(initialRemoteStreamWindow, childChannel.config().getWriteBufferHighWaterMark()); - codec.onHttp2StreamWritabilityChanged(stream, true); assertTrue(childChannel.isWritable()); - codec.onHttp2StreamWritabilityChanged(stream, false); + childChannel.write(new DefaultHttp2DataFrame(Unpooled.buffer().writeZero(16 * 1024 * 1024))); assertFalse(childChannel.isWritable()); } @Test public void writabilityAndFlowControl() { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + LastInboundHandler inboundHandler = new LastInboundHandler(); + Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); assertEquals("", inboundHandler.writabilityStates()); + assertTrue(childChannel.isWritable()); // HEADERS frames are not flow controlled, so they should not affect the flow control window. childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); - codec.onHttp2StreamWritabilityChanged(childChannel.stream(), true); + codec.onHttp2StreamWritabilityChanged(codec.ctx, childChannel.stream(), true); - assertEquals("true", inboundHandler.writabilityStates()); + assertTrue(childChannel.isWritable()); + assertEquals("", inboundHandler.writabilityStates()); - codec.onHttp2StreamWritabilityChanged(childChannel.stream(), true); - assertEquals("true", inboundHandler.writabilityStates()); + codec.onHttp2StreamWritabilityChanged(codec.ctx, childChannel.stream(), true); + assertTrue(childChannel.isWritable()); + assertEquals("", inboundHandler.writabilityStates()); - codec.onHttp2StreamWritabilityChanged(childChannel.stream(), false); - assertEquals("true,false", inboundHandler.writabilityStates()); + codec.onHttp2StreamWritabilityChanged(codec.ctx, childChannel.stream(), false); + assertFalse(childChannel.isWritable()); + assertEquals("false", inboundHandler.writabilityStates()); - codec.onHttp2StreamWritabilityChanged(childChannel.stream(), false); - assertEquals("true,false", inboundHandler.writabilityStates()); + codec.onHttp2StreamWritabilityChanged(codec.ctx, childChannel.stream(), false); + assertFalse(childChannel.isWritable()); + assertEquals("false", inboundHandler.writabilityStates()); } @Test public void channelClosedWhenInactiveFired() { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + LastInboundHandler inboundHandler = new LastInboundHandler(); + Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); final AtomicBoolean channelOpen = new AtomicBoolean(false); final AtomicBoolean channelActive = new AtomicBoolean(false); @@ -725,9 +738,7 @@ public void channelInactiveHappensAfterExceptionCaughtEvents() throws Exception final AtomicInteger exceptionCaught = new AtomicInteger(-1); final AtomicInteger channelInactive = new AtomicInteger(-1); final AtomicInteger channelUnregistered = new AtomicInteger(-1); - Http2StreamChannel childChannel = newOutboundStream(); - - childChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() { + Http2StreamChannel childChannel = newOutboundStream(new ChannelInboundHandlerAdapter() { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { @@ -767,28 +778,10 @@ public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { assertEquals(2, channelUnregistered.get()); } - @Ignore("not supported anymore atm") - @Test - public void cancellingWritesBeforeFlush() { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - Channel childChannel = inboundHandler.channel(); - - Http2HeadersFrame headers1 = new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()); - Http2HeadersFrame headers2 = new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()); - ChannelPromise writePromise = childChannel.newPromise(); - childChannel.write(headers1, writePromise); - childChannel.write(headers2); - assertTrue(writePromise.cancel(false)); - childChannel.flush(); - - Http2HeadersFrame headers = parentChannel.readOutbound(); - assertSame(headers, headers2); - } - @Test public void callUnsafeCloseMultipleTimes() { - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream); - Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + LastInboundHandler inboundHandler = new LastInboundHandler(); + Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); childChannel.unsafe().close(childChannel.voidPromise()); ChannelPromise promise = childChannel.newPromise(); @@ -809,49 +802,56 @@ public void accept(ChannelHandlerContext obj) { } } }; - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream, numReads, ctxConsumer); - Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + LastInboundHandler inboundHandler = new LastInboundHandler(ctxConsumer); + Http2StreamChannel childChannel = newInboundStream(3, false, numReads, inboundHandler); childChannel.config().setAutoRead(false); - Http2DataFrame dataFrame1 = new DefaultHttp2DataFrame(bb("1")).stream(inboundStream); - Http2DataFrame dataFrame2 = new DefaultHttp2DataFrame(bb("2")).stream(inboundStream); - Http2DataFrame dataFrame3 = new DefaultHttp2DataFrame(bb("3")).stream(inboundStream); - Http2DataFrame dataFrame4 = new DefaultHttp2DataFrame(bb("4")).stream(inboundStream); + Http2DataFrame dataFrame1 = new DefaultHttp2DataFrame(bb("1")).stream(childChannel.stream()); + Http2DataFrame dataFrame2 = new DefaultHttp2DataFrame(bb("2")).stream(childChannel.stream()); + Http2DataFrame dataFrame3 = new DefaultHttp2DataFrame(bb("3")).stream(childChannel.stream()); + Http2DataFrame dataFrame4 = new DefaultHttp2DataFrame(bb("4")).stream(childChannel.stream()); - assertEquals(new DefaultHttp2HeadersFrame(request).stream(inboundStream), inboundHandler.readInbound()); + assertEquals(new DefaultHttp2HeadersFrame(request).stream(childChannel.stream()), inboundHandler.readInbound()); + + ChannelHandler readCompleteSupressHandler = new ChannelInboundHandlerAdapter() { + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + // We want to simulate the parent channel calling channelRead and delay calling channelReadComplete. + } + }; - // We want to simulate the parent channel calling channelRead and delay calling channelReadComplete. - parentChannel.writeOneInbound(new Object()); - codec.onHttp2Frame(dataFrame1); - assertEquals(dataFrame1, inboundHandler.readInbound()); + parentChannel.pipeline().addFirst(readCompleteSupressHandler); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("1"), 0, false); + + assertEqualsAndRelease(dataFrame1, inboundHandler.readInbound()); // Deliver frames, and then a stream closed while read is inactive. - codec.onHttp2Frame(dataFrame2); - codec.onHttp2Frame(dataFrame3); - codec.onHttp2Frame(dataFrame4); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("2"), 0, false); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("3"), 0, false); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("4"), 0, false); shouldDisableAutoRead.set(true); childChannel.config().setAutoRead(true); numReads.set(1); - inboundStream.state = Http2Stream.State.CLOSED; - codec.onHttp2StreamStateChanged(inboundStream); + frameInboundWriter.writeInboundRstStream(childChannel.stream().id(), Http2Error.NO_ERROR.code()); // Detecting EOS should flush all pending data regardless of read calls. - assertEquals(dataFrame2, inboundHandler.readInbound()); - assertEquals(dataFrame3, inboundHandler.readInbound()); - assertEquals(dataFrame4, inboundHandler.readInbound()); + assertEqualsAndRelease(dataFrame2, inboundHandler.readInbound()); + assertEqualsAndRelease(dataFrame3, inboundHandler.readInbound()); + assertEqualsAndRelease(dataFrame4, inboundHandler.readInbound()); + + Http2ResetFrame resetFrame = inboundHandler.readInbound(); + assertEquals(childChannel.stream(), resetFrame.stream()); + assertEquals(Http2Error.NO_ERROR.code(), resetFrame.errorCode()); + assertNull(inboundHandler.readInbound()); // Now we want to call channelReadComplete and simulate the end of the read loop. + parentChannel.pipeline().remove(readCompleteSupressHandler); parentChannel.flushInbound(); childChannel.closeFuture().syncUninterruptibly(); - - dataFrame1.release(); - dataFrame2.release(); - dataFrame3.release(); - dataFrame4.release(); } @Test @@ -868,54 +868,58 @@ public void accept(ChannelHandlerContext obj) { } } }; - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream, numReads, ctxConsumer); - Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + LastInboundHandler inboundHandler = new LastInboundHandler(ctxConsumer); + Http2StreamChannel childChannel = newInboundStream(3, false, numReads, inboundHandler); childChannel.config().setAutoRead(false); - Http2DataFrame dataFrame1 = new DefaultHttp2DataFrame(bb("1")).stream(inboundStream); - Http2DataFrame dataFrame2 = new DefaultHttp2DataFrame(bb("2")).stream(inboundStream); - Http2DataFrame dataFrame3 = new DefaultHttp2DataFrame(bb("3")).stream(inboundStream); - Http2DataFrame dataFrame4 = new DefaultHttp2DataFrame(bb("4")).stream(inboundStream); + Http2DataFrame dataFrame1 = new DefaultHttp2DataFrame(bb("1")).stream(childChannel.stream()); + Http2DataFrame dataFrame2 = new DefaultHttp2DataFrame(bb("2")).stream(childChannel.stream()); + Http2DataFrame dataFrame3 = new DefaultHttp2DataFrame(bb("3")).stream(childChannel.stream()); + Http2DataFrame dataFrame4 = new DefaultHttp2DataFrame(bb("4")).stream(childChannel.stream()); + + assertEquals(new DefaultHttp2HeadersFrame(request).stream(childChannel.stream()), inboundHandler.readInbound()); + + ChannelHandler readCompleteSupressHandler = new ChannelInboundHandlerAdapter() { + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + // We want to simulate the parent channel calling channelRead and delay calling channelReadComplete. + } + }; + parentChannel.pipeline().addFirst(readCompleteSupressHandler); - assertEquals(new DefaultHttp2HeadersFrame(request).stream(inboundStream), inboundHandler.readInbound()); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("1"), 0, false); - // We want to simulate the parent channel calling channelRead and delay calling channelReadComplete. - parentChannel.writeOneInbound(new Object()); - codec.onHttp2Frame(dataFrame1); - assertEquals(dataFrame1, inboundHandler.readInbound()); + assertEqualsAndRelease(dataFrame1, inboundHandler.readInbound()); // We want one item to be in the queue, and allow the numReads to be larger than 1. This will ensure that // when beginRead() is called the child channel is added to the readPending queue of the parent channel. - codec.onHttp2Frame(dataFrame2); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("2"), 0, false); numReads.set(10); shouldDisableAutoRead.set(true); childChannel.config().setAutoRead(true); - codec.onHttp2Frame(dataFrame3); - codec.onHttp2Frame(dataFrame4); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("3"), 0, false); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("4"), 0, false); // Detecting EOS should flush all pending data regardless of read calls. - assertEquals(dataFrame2, inboundHandler.readInbound()); - assertEquals(dataFrame3, inboundHandler.readInbound()); - assertEquals(dataFrame4, inboundHandler.readInbound()); + assertEqualsAndRelease(dataFrame2, inboundHandler.readInbound()); + assertEqualsAndRelease(dataFrame3, inboundHandler.readInbound()); + assertEqualsAndRelease(dataFrame4, inboundHandler.readInbound()); + assertNull(inboundHandler.readInbound()); // Now we want to call channelReadComplete and simulate the end of the read loop. + parentChannel.pipeline().remove(readCompleteSupressHandler); parentChannel.flushInbound(); // 3 = 1 for initialization + 1 for read when auto read was off + 1 for when auto read was back on assertEquals(3, channelReadCompleteCount.get()); - - dataFrame1.release(); - dataFrame2.release(); - dataFrame3.release(); - dataFrame4.release(); } @Test public void childQueueIsDrainedAndNewDataIsDispatchedInParentReadLoopNoAutoRead() { - AtomicInteger numReads = new AtomicInteger(1); + final AtomicInteger numReads = new AtomicInteger(1); final AtomicInteger channelReadCompleteCount = new AtomicInteger(0); final AtomicBoolean shouldDisableAutoRead = new AtomicBoolean(); Consumer ctxConsumer = new Consumer() { @@ -927,214 +931,77 @@ public void accept(ChannelHandlerContext obj) { } } }; - LastInboundHandler inboundHandler = streamActiveAndWriteHeaders(inboundStream, numReads, ctxConsumer); - Http2StreamChannel childChannel = (Http2StreamChannel) inboundHandler.channel(); + final LastInboundHandler inboundHandler = new LastInboundHandler(ctxConsumer); + Http2StreamChannel childChannel = newInboundStream(3, false, numReads, inboundHandler); childChannel.config().setAutoRead(false); - Http2DataFrame dataFrame1 = new DefaultHttp2DataFrame(bb("1")).stream(inboundStream); - Http2DataFrame dataFrame2 = new DefaultHttp2DataFrame(bb("2")).stream(inboundStream); - Http2DataFrame dataFrame3 = new DefaultHttp2DataFrame(bb("3")).stream(inboundStream); - Http2DataFrame dataFrame4 = new DefaultHttp2DataFrame(bb("4")).stream(inboundStream); + Http2DataFrame dataFrame1 = new DefaultHttp2DataFrame(bb("1")).stream(childChannel.stream()); + Http2DataFrame dataFrame2 = new DefaultHttp2DataFrame(bb("2")).stream(childChannel.stream()); + Http2DataFrame dataFrame3 = new DefaultHttp2DataFrame(bb("3")).stream(childChannel.stream()); + Http2DataFrame dataFrame4 = new DefaultHttp2DataFrame(bb("4")).stream(childChannel.stream()); + + assertEquals(new DefaultHttp2HeadersFrame(request).stream(childChannel.stream()), inboundHandler.readInbound()); - assertEquals(new DefaultHttp2HeadersFrame(request).stream(inboundStream), inboundHandler.readInbound()); + ChannelHandler readCompleteSupressHandler = new ChannelInboundHandlerAdapter() { + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + // We want to simulate the parent channel calling channelRead and delay calling channelReadComplete. + } + }; + parentChannel.pipeline().addFirst(readCompleteSupressHandler); + + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("1"), 0, false); - // We want to simulate the parent channel calling channelRead and delay calling channelReadComplete. - parentChannel.writeOneInbound(new Object()); - codec.onHttp2Frame(dataFrame1); - assertEquals(dataFrame1, inboundHandler.readInbound()); + assertEqualsAndRelease(dataFrame1, inboundHandler.readInbound()); // We want one item to be in the queue, and allow the numReads to be larger than 1. This will ensure that // when beginRead() is called the child channel is added to the readPending queue of the parent channel. - codec.onHttp2Frame(dataFrame2); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("2"), 0, false); numReads.set(2); childChannel.read(); - assertEquals(dataFrame2, inboundHandler.readInbound()); + + assertEqualsAndRelease(dataFrame2, inboundHandler.readInbound()); + assertNull(inboundHandler.readInbound()); // This is the second item that was read, this should be the last until we call read() again. This should also // notify of readComplete(). - codec.onHttp2Frame(dataFrame3); - assertEquals(dataFrame3, inboundHandler.readInbound()); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("3"), 0, false); + + assertEqualsAndRelease(dataFrame3, inboundHandler.readInbound()); - codec.onHttp2Frame(dataFrame4); + frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("4"), 0, false); assertNull(inboundHandler.readInbound()); childChannel.read(); - assertEquals(dataFrame4, inboundHandler.readInbound()); + + assertEqualsAndRelease(dataFrame4, inboundHandler.readInbound()); + assertNull(inboundHandler.readInbound()); // Now we want to call channelReadComplete and simulate the end of the read loop. + parentChannel.pipeline().remove(readCompleteSupressHandler); parentChannel.flushInbound(); // 3 = 1 for initialization + 1 for first read of 2 items + 1 for second read of 2 items + // 1 for parent channel readComplete assertEquals(4, channelReadCompleteCount.get()); - - dataFrame1.release(); - dataFrame2.release(); - dataFrame3.release(); - dataFrame4.release(); - } - - private LastInboundHandler streamActiveAndWriteHeaders(Http2FrameStream stream) { - return streamActiveAndWriteHeaders(stream, null, LastInboundHandler.noopConsumer()); - } - - private LastInboundHandler streamActiveAndWriteHeaders(Http2FrameStream stream, - AtomicInteger maxReads, - Consumer contextConsumer) { - - LastInboundHandler inboundHandler = new LastInboundHandler(contextConsumer); - childChannelInitializer.handler = inboundHandler; - childChannelInitializer.maxReads = maxReads; - assertFalse(inboundHandler.isChannelActive()); - ((TestableHttp2MultiplexCodec.Stream) stream).state = Http2Stream.State.OPEN; - codec.onHttp2StreamStateChanged(stream); - codec.onHttp2Frame(new DefaultHttp2HeadersFrame(request).stream(stream)); - codec.onChannelReadComplete(); - assertTrue(inboundHandler.isChannelActive()); - - return inboundHandler; } - private static void verifyFramesMultiplexedToCorrectChannel(Http2FrameStream stream, + private static void verifyFramesMultiplexedToCorrectChannel(Http2StreamChannel streamChannel, LastInboundHandler inboundHandler, int numFrames) { for (int i = 0; i < numFrames; i++) { Http2StreamFrame frame = inboundHandler.readInbound(); assertNotNull(frame); - assertEquals(stream, frame.stream()); + assertEquals(streamChannel.stream(), frame.stream()); release(frame); } assertNull(inboundHandler.readInbound()); } - private static ByteBuf bb(String s) { - return ByteBufUtil.writeUtf8(UnpooledByteBufAllocator.DEFAULT, s); - } - - /** - * Simulates the frame codec, in first assigning an identifier and the completing the write promise. - */ - private Http2FrameStream readOutboundHeadersAndAssignId() { - // Only peek at the frame, so to not complete the promise of the write. We need to first - // assign a stream identifier, as the frame codec would do. - Http2HeadersFrame headersFrame = (Http2HeadersFrame) parentChannel.outboundMessages().peek(); - assertNotNull(headersFrame); - assertNotNull(headersFrame.stream()); - assertFalse(Http2CodecUtil.isStreamIdValid(headersFrame.stream().id())); - TestableHttp2MultiplexCodec.Stream frameStream = (TestableHttp2MultiplexCodec.Stream) headersFrame.stream(); - frameStream.id = outboundStream.id(); - // Create the stream in the Http2Connection. - try { - Http2Stream stream = codec.connection().local().createStream( - headersFrame.stream().id(), headersFrame.isEndStream()); - frameStream.stream = stream; - } catch (Exception ex) { - throw new IllegalStateException("Failed to create a stream", ex); - } - - // Now read it and complete the write promise. - assertSame(headersFrame, parentChannel.readOutbound()); - - return headersFrame.stream(); - } - - /** - * This class removes the bits that would transform the frames to bytes and so make it easier to test the actual - * special handling of the codec. - */ - private final class TestableHttp2MultiplexCodec extends Http2MultiplexCodec { - - public TestableHttp2MultiplexCodec(Http2ConnectionEncoder encoder, - Http2ConnectionDecoder decoder, - Http2Settings initialSettings, - ChannelHandler inboundStreamHandler) { - super(encoder, decoder, initialSettings, inboundStreamHandler, null); - } - - void onHttp2Frame(Http2Frame frame) { - onHttp2Frame(ctx, frame); - } - - void onChannelReadComplete() { - onChannelReadComplete(ctx); - } - - void onHttp2StreamStateChanged(Http2FrameStream stream) { - onHttp2StreamStateChanged(ctx, stream); - } - - void onHttp2FrameStreamException(Http2FrameStreamException cause) { - onHttp2FrameStreamException(ctx, cause); - } - - void onHttp2StreamWritabilityChanged(Http2FrameStream stream, boolean writable) { - onHttp2StreamWritabilityChanged(ctx, stream, writable); - } - - @Override - boolean onBytesConsumed(ChannelHandlerContext ctx, Http2FrameStream stream, int bytes) { - writer.write(new DefaultHttp2WindowUpdateFrame(bytes).stream(stream), ctx.newPromise()); - return true; - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { - writer.write(msg, promise); - } - - @Override - void flush0(ChannelHandlerContext ctx) { - // Do nothing - } - - @Override - Stream newStream() { - return new Stream(); - } - - final class Stream extends Http2MultiplexCodecStream { - Http2Stream.State state = Http2Stream.State.IDLE; - int id = -1; - - @Override - public int id() { - return id; - } - - @Override - public Http2Stream.State state() { - return state; - } - } - } - - private final class TestableHttp2MultiplexCodecBuilder extends Http2MultiplexCodecBuilder { - - TestableHttp2MultiplexCodecBuilder(boolean server, ChannelHandler childHandler) { - super(server, childHandler); - } - - @Override - public TestableHttp2MultiplexCodec build() { - return (TestableHttp2MultiplexCodec) super.build(); - } - - @Override - protected Http2MultiplexCodec build( - Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, Http2Settings initialSettings) { - return new TestableHttp2MultiplexCodec( - encoder, decoder, initialSettings, childHandler); - } - } - - class Writer { - - void write(Object msg, ChannelPromise promise) { - parentChannel.outboundMessages().add(msg); - promise.setSuccess(); - } + private static int eqStreamId(Http2StreamChannel channel) { + return eq(channel.stream().id()); } } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java index e7130ee5d902..f6603812374c 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java @@ -15,7 +15,9 @@ package io.netty.handler.codec.http2; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; +import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -24,18 +26,34 @@ import io.netty.channel.DefaultChannelPromise; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.util.AsciiString; +import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.ImmediateEventExecutor; import junit.framework.AssertionFailedError; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.util.List; import java.util.Random; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_LIST_SIZE; import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_TABLE_SIZE; +import static io.netty.util.ReferenceCountUtil.release; import static java.lang.Math.min; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyByte; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyShort; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; /** * Utilities for the integration tests. @@ -506,4 +524,179 @@ public int windowSize() { return isWriteAllowed ? (int) min(pendingBytes, Integer.MAX_VALUE) : -1; } } + + static Http2FrameWriter mockedFrameWriter() { + Http2FrameWriter.Configuration configuration = new Http2FrameWriter.Configuration() { + private final Http2HeadersEncoder.Configuration headerConfiguration = + new Http2HeadersEncoder.Configuration() { + @Override + public void maxHeaderTableSize(long max) { + // NOOP + } + + @Override + public long maxHeaderTableSize() { + return 0; + } + + @Override + public void maxHeaderListSize(long max) { + // NOOP + } + + @Override + public long maxHeaderListSize() { + return 0; + } + }; + + private final Http2FrameSizePolicy policy = new Http2FrameSizePolicy() { + @Override + public void maxFrameSize(int max) { + // NOOP + } + + @Override + public int maxFrameSize() { + return 0; + } + }; + @Override + public Http2HeadersEncoder.Configuration headersConfiguration() { + return headerConfiguration; + } + + @Override + public Http2FrameSizePolicy frameSizePolicy() { + return policy; + } + }; + + final ConcurrentLinkedQueue buffers = new ConcurrentLinkedQueue(); + + Http2FrameWriter frameWriter = Mockito.mock(Http2FrameWriter.class); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) { + for (;;) { + ByteBuf buf = buffers.poll(); + if (buf == null) { + break; + } + buf.release(); + } + return null; + } + }).when(frameWriter).close(); + + when(frameWriter.configuration()).thenReturn(configuration); + when(frameWriter.writeSettings(any(ChannelHandlerContext.class), any(Http2Settings.class), + any(ChannelPromise.class))).thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + return ((ChannelPromise) invocationOnMock.getArgument(2)).setSuccess(); + } + }); + + when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class), any(ChannelPromise.class))) + .thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + return ((ChannelPromise) invocationOnMock.getArgument(1)).setSuccess(); + } + }); + + when(frameWriter.writeGoAway(any(ChannelHandlerContext.class), anyInt(), + anyLong(), any(ByteBuf.class), any(ChannelPromise.class))).thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + buffers.offer((ByteBuf) invocationOnMock.getArgument(3)); + return ((ChannelPromise) invocationOnMock.getArgument(4)).setSuccess(); + } + }); + when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(), + anyBoolean(), any(ChannelPromise.class))).thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + return ((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess(); + } + }); + + when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), + any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), + any(ChannelPromise.class))).thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + return ((ChannelPromise) invocationOnMock.getArgument(8)).setSuccess(); + } + }); + + when(frameWriter.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), + anyBoolean(), any(ChannelPromise.class))).thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + buffers.offer((ByteBuf) invocationOnMock.getArgument(2)); + return ((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess(); + } + }); + + when(frameWriter.writeRstStream(any(ChannelHandlerContext.class), anyInt(), + anyLong(), any(ChannelPromise.class))).thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + return ((ChannelPromise) invocationOnMock.getArgument(3)).setSuccess(); + } + }); + + when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(), + any(ChannelPromise.class))).then(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + return ((ChannelPromise) invocationOnMock.getArgument(3)).setSuccess(); + } + }); + + when(frameWriter.writePushPromise(any(ChannelHandlerContext.class), anyInt(), anyInt(), any(Http2Headers.class), + anyInt(), anyChannelPromise())).thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + return ((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess(); + } + }); + + when(frameWriter.writeFrame(any(ChannelHandlerContext.class), anyByte(), anyInt(), any(Http2Flags.class), + any(ByteBuf.class), anyChannelPromise())).thenAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock invocationOnMock) { + buffers.offer((ByteBuf) invocationOnMock.getArgument(4)); + return ((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess(); + } + }); + return frameWriter; + } + + static ChannelPromise anyChannelPromise() { + return any(ChannelPromise.class); + } + + static Http2Settings anyHttp2Settings() { + return any(Http2Settings.class); + } + + static ByteBuf bb(String s) { + return ByteBufUtil.writeUtf8(UnpooledByteBufAllocator.DEFAULT, s); + } + + static void assertEqualsAndRelease(Http2Frame expected, Http2Frame actual) { + try { + assertEquals(expected, actual); + } finally { + release(expected); + release(actual); + // Will return -1 when not implements ReferenceCounted. + assertTrue(ReferenceCountUtil.refCnt(expected) <= 0); + assertTrue(ReferenceCountUtil.refCnt(actual) <= 0); + } + } + } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java index 015550f078d1..2cc79d14e579 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java @@ -50,9 +50,9 @@ public void initChannel(Channel channel) { /** * Designed to read a single byte at a time to control the number of reads done at a fine granularity. */ - private static final class TestNumReadsRecvByteBufAllocator implements RecvByteBufAllocator { + static final class TestNumReadsRecvByteBufAllocator implements RecvByteBufAllocator { private final AtomicInteger numReads; - TestNumReadsRecvByteBufAllocator(AtomicInteger numReads) { + private TestNumReadsRecvByteBufAllocator(AtomicInteger numReads) { this.numReads = numReads; } From 66addd485f42080dca190eb7c8896ed1acd45015 Mon Sep 17 00:00:00 2001 From: kezhenxu94 Date: Mon, 14 Jan 2019 14:20:57 +0800 Subject: [PATCH 327/417] Use camel-case in NioEventLoop (#8713) Motivation: Java uses camel-case by convention. Modification: Consistently use camel-case. Result: More consistent code styling. --- .../main/java/io/netty/channel/nio/NioEventLoop.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java index 16695c8f7e52..100065f3503b 100644 --- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java @@ -59,7 +59,7 @@ public final class NioEventLoop extends SingleThreadEventLoop { private static final int CLEANUP_INTERVAL = 256; // XXX Hard-coded value, but won't need customization. - private static final boolean DISABLE_KEYSET_OPTIMIZATION = + private static final boolean DISABLE_KEY_SET_OPTIMIZATION = SystemPropertyUtil.getBoolean("io.netty.noKeySetOptimization", false); private static final int MIN_PREMATURE_SELECTOR_RETURNS = 3; @@ -79,8 +79,8 @@ public int get() throws Exception { // - https://github.com/netty/netty/issues/203 static { final String key = "sun.nio.ch.bugLevel"; - final String buglevel = SystemPropertyUtil.get(key); - if (buglevel == null) { + final String bugLevel = SystemPropertyUtil.get(key); + if (bugLevel == null) { try { AccessController.doPrivileged(new PrivilegedAction() { @Override @@ -102,7 +102,7 @@ public Void run() { SELECTOR_AUTO_REBUILD_THRESHOLD = selectorAutoRebuildThreshold; if (logger.isDebugEnabled()) { - logger.debug("-Dio.netty.noKeySetOptimization: {}", DISABLE_KEYSET_OPTIMIZATION); + logger.debug("-Dio.netty.noKeySetOptimization: {}", DISABLE_KEY_SET_OPTIMIZATION); logger.debug("-Dio.netty.selectorAutoRebuildThreshold: {}", SELECTOR_AUTO_REBUILD_THRESHOLD); } } @@ -169,7 +169,7 @@ private SelectorTuple openSelector() { throw new ChannelException("failed to open a new selector", e); } - if (DISABLE_KEYSET_OPTIMIZATION) { + if (DISABLE_KEY_SET_OPTIMIZATION) { return new SelectorTuple(unwrappedSelector); } From 6fdd7fcddbe964b2f30d7492a926f4f0bf0f083f Mon Sep 17 00:00:00 2001 From: kashike Date: Sun, 13 Jan 2019 22:24:34 -0800 Subject: [PATCH 328/417] Fix minor spelling issues in javadocs (#8701) Motivation: Javadocs contained some spelling errors, we should fix these. Modification: Fix spelling Result: Javadoc cleanup. --- .../io/netty/buffer/CompositeByteBuf.java | 42 +++++++++---------- .../main/java/io/netty/buffer/Unpooled.java | 6 +-- .../netty/handler/codec/http/HttpHeaders.java | 2 +- .../handler/codec/http2/Http2DataFrame.java | 2 +- .../handler/codec/ByteToMessageDecoder.java | 2 +- .../codec/serialization/ClassResolvers.java | 8 ++-- .../java/io/netty/util/HashingStrategy.java | 2 +- .../ssl/ReferenceCountedOpenSslEngine.java | 2 +- .../resolver/dns/DnsNameResolverTest.java | 8 ++-- .../channel/kqueue/KQueueChannelConfig.java | 4 +- .../channel/kqueue/KQueueChannelOption.java | 2 +- .../java/io/netty/channel/kqueue/Native.java | 2 +- .../channel/unix/DomainSocketReadMode.java | 6 ++- .../AbstractCoalescingBufferQueue.java | 2 +- .../io/netty/channel/DefaultFileRegion.java | 4 +- .../java/io/netty/channel/FileRegion.java | 4 +- .../socket/oio/OioDatagramChannel.java | 2 +- 17 files changed, 51 insertions(+), 49 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index ce3c9f7f41ea..96c14cd28fee 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -158,8 +158,8 @@ private static Component[] newCompArray(int initComponents, int maxNumComponents * Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}. * If you need to have it increased use {@link #addComponent(boolean, ByteBuf)}. *

- * {@link ByteBuf#release()} ownership of {@code buffer} is transfered to this {@link CompositeByteBuf}. - * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transfered to this + * {@link ByteBuf#release()} ownership of {@code buffer} is transferred to this {@link CompositeByteBuf}. + * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transferred to this * {@link CompositeByteBuf}. */ public CompositeByteBuf addComponent(ByteBuf buffer) { @@ -172,10 +172,10 @@ public CompositeByteBuf addComponent(ByteBuf buffer) { * Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}. * If you need to have it increased use {@link #addComponents(boolean, ByteBuf[])}. *

- * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this + * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this * {@link CompositeByteBuf}. * @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()} - * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}. + * ownership of all {@link ByteBuf} objects is transferred to this {@link CompositeByteBuf}. */ public CompositeByteBuf addComponents(ByteBuf... buffers) { return addComponents(false, buffers); @@ -187,10 +187,10 @@ public CompositeByteBuf addComponents(ByteBuf... buffers) { * Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}. * If you need to have it increased use {@link #addComponents(boolean, Iterable)}. *

- * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this + * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this * {@link CompositeByteBuf}. * @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()} - * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}. + * ownership of all {@link ByteBuf} objects is transferred to this {@link CompositeByteBuf}. */ public CompositeByteBuf addComponents(Iterable buffers) { return addComponents(false, buffers); @@ -202,9 +202,9 @@ public CompositeByteBuf addComponents(Iterable buffers) { * Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}. * If you need to have it increased use {@link #addComponent(boolean, int, ByteBuf)}. *

- * {@link ByteBuf#release()} ownership of {@code buffer} is transfered to this {@link CompositeByteBuf}. + * {@link ByteBuf#release()} ownership of {@code buffer} is transferred to this {@link CompositeByteBuf}. * @param cIndex the index on which the {@link ByteBuf} will be added. - * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transfered to this + * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transferred to this * {@link CompositeByteBuf}. */ public CompositeByteBuf addComponent(int cIndex, ByteBuf buffer) { @@ -215,8 +215,8 @@ public CompositeByteBuf addComponent(int cIndex, ByteBuf buffer) { * Add the given {@link ByteBuf} and increase the {@code writerIndex} if {@code increaseWriterIndex} is * {@code true}. * - * {@link ByteBuf#release()} ownership of {@code buffer} is transfered to this {@link CompositeByteBuf}. - * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transfered to this + * {@link ByteBuf#release()} ownership of {@code buffer} is transferred to this {@link CompositeByteBuf}. + * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transferred to this * {@link CompositeByteBuf}. */ public CompositeByteBuf addComponent(boolean increaseWriterIndex, ByteBuf buffer) { @@ -230,10 +230,10 @@ public CompositeByteBuf addComponent(boolean increaseWriterIndex, ByteBuf buffer * Add the given {@link ByteBuf}s and increase the {@code writerIndex} if {@code increaseWriterIndex} is * {@code true}. * - * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this + * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this * {@link CompositeByteBuf}. * @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()} - * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}. + * ownership of all {@link ByteBuf} objects is transferred to this {@link CompositeByteBuf}. */ public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... buffers) { checkNotNull(buffers, "buffers"); @@ -246,10 +246,10 @@ public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... bu * Add the given {@link ByteBuf}s and increase the {@code writerIndex} if {@code increaseWriterIndex} is * {@code true}. * - * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this + * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this * {@link CompositeByteBuf}. * @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()} - * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}. + * ownership of all {@link ByteBuf} objects is transferred to this {@link CompositeByteBuf}. */ public CompositeByteBuf addComponents(boolean increaseWriterIndex, Iterable buffers) { addComponents0(increaseWriterIndex, componentCount, buffers); @@ -261,9 +261,9 @@ public CompositeByteBuf addComponents(boolean increaseWriterIndex, Iterable - * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this + * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this * {@link CompositeByteBuf}. * @param cIndex the index on which the {@link ByteBuf} will be added. {@link ByteBuf#release()} ownership of all - * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects is transfered to this + * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects is transferred to this * {@link CompositeByteBuf}. * @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()} - * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}. + * ownership of all {@link ByteBuf} objects is transferred to this {@link CompositeByteBuf}. */ public CompositeByteBuf addComponents(int cIndex, ByteBuf... buffers) { checkNotNull(buffers, "buffers"); @@ -405,11 +405,11 @@ private int addComponents0(boolean increaseWriterIndex, int cIndex, * Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}. * If you need to have it increased you need to handle it by your own. *

- * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this + * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this * {@link CompositeByteBuf}. * @param cIndex the index on which the {@link ByteBuf} will be added. * @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all - * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects is transfered to this + * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects is transferred to this * {@link CompositeByteBuf}. */ public CompositeByteBuf addComponents(int cIndex, Iterable buffers) { diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/netty/buffer/Unpooled.java index 8c7dc77e564b..d7df1928857e 100644 --- a/buffer/src/main/java/io/netty/buffer/Unpooled.java +++ b/buffer/src/main/java/io/netty/buffer/Unpooled.java @@ -219,7 +219,7 @@ public static ByteBuf wrappedBuffer(long memoryAddress, int size, boolean doFree * Creates a new buffer which wraps the specified buffer's readable bytes. * A modification on the specified buffer's content will be visible to the * returned buffer. - * @param buffer The buffer to wrap. Reference count ownership of this variable is transfered to this method. + * @param buffer The buffer to wrap. Reference count ownership of this variable is transferred to this method. * @return The readable portion of the {@code buffer}, or an empty buffer if there is no readable portion. * The caller is responsible for releasing this buffer. */ @@ -245,7 +245,7 @@ public static ByteBuf wrappedBuffer(byte[]... arrays) { * Creates a new big-endian composite buffer which wraps the readable bytes of the * specified buffers without copying them. A modification on the content * of the specified buffers will be visible to the returned buffer. - * @param buffers The buffers to wrap. Reference count ownership of all variables is transfered to this method. + * @param buffers The buffers to wrap. Reference count ownership of all variables is transferred to this method. * @return The readable portion of the {@code buffers}. The caller is responsible for releasing this buffer. */ public static ByteBuf wrappedBuffer(ByteBuf... buffers) { @@ -300,7 +300,7 @@ public static ByteBuf wrappedBuffer(int maxNumComponents, byte[]... arrays) { * of the specified buffers will be visible to the returned buffer. * @param maxNumComponents Advisement as to how many independent buffers are allowed to exist before * consolidation occurs. - * @param buffers The buffers to wrap. Reference count ownership of all variables is transfered to this method. + * @param buffers The buffers to wrap. Reference count ownership of all variables is transferred to this method. * @return The readable portion of the {@code buffers}. The caller is responsible for releasing this buffer. */ public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuf... buffers) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java index 35cfc5c5f181..694bc4d56e44 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java @@ -1695,7 +1695,7 @@ public String toString() { } /** - * Returns a deap copy of the passed in {@link HttpHeaders}. + * Returns a deep copy of the passed in {@link HttpHeaders}. */ public HttpHeaders copy() { return new DefaultHttpHeaders().set(this); diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataFrame.java index a0d55c487984..2c99e2c287a4 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataFrame.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataFrame.java @@ -37,7 +37,7 @@ public interface Http2DataFrame extends Http2StreamFrame, ByteBufHolder { ByteBuf content(); /** - * Returns the number of bytes that are flow-controlled initialy, so even if the {@link #content()} is consumed + * Returns the number of bytes that are flow-controlled initially, so even if the {@link #content()} is consumed * this will not change. */ int initialFlowControlledBytes(); diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java index fec73f8fc9a3..ddf3d9b14fae 100644 --- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java @@ -134,7 +134,7 @@ public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) return buffer; } finally { if (in != null) { - // We must release if the ownership was not transfered as otherwise it may produce a leak if + // We must release if the ownership was not transferred as otherwise it may produce a leak if // writeBytes(...) throw for whatever release (for example because of OutOfMemoryError). in.release(); } diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/ClassResolvers.java b/codec/src/main/java/io/netty/handler/codec/serialization/ClassResolvers.java index 0d8c11f2f9f3..32250ada801b 100644 --- a/codec/src/main/java/io/netty/handler/codec/serialization/ClassResolvers.java +++ b/codec/src/main/java/io/netty/handler/codec/serialization/ClassResolvers.java @@ -32,7 +32,7 @@ public static ClassResolver cacheDisabled(ClassLoader classLoader) { } /** - * non-agressive non-concurrent cache + * non-aggressive non-concurrent cache * good for non-shared default cache * * @param classLoader - specific classLoader to use, or null if you want to revert to default @@ -45,7 +45,7 @@ public static ClassResolver weakCachingResolver(ClassLoader classLoader) { } /** - * agressive non-concurrent cache + * aggressive non-concurrent cache * good for non-shared cache, when we're not worried about class unloading * * @param classLoader - specific classLoader to use, or null if you want to revert to default @@ -58,7 +58,7 @@ public static ClassResolver softCachingResolver(ClassLoader classLoader) { } /** - * non-agressive concurrent cache + * non-aggressive concurrent cache * good for shared cache, when we're worried about class unloading * * @param classLoader - specific classLoader to use, or null if you want to revert to default @@ -72,7 +72,7 @@ public static ClassResolver weakCachingConcurrentResolver(ClassLoader classLoade } /** - * agressive concurrent cache + * aggressive concurrent cache * good for shared cache, when we're not worried about class unloading * * @param classLoader - specific classLoader to use, or null if you want to revert to default diff --git a/common/src/main/java/io/netty/util/HashingStrategy.java b/common/src/main/java/io/netty/util/HashingStrategy.java index f15f4a3299ba..957d765c4622 100644 --- a/common/src/main/java/io/netty/util/HashingStrategy.java +++ b/common/src/main/java/io/netty/util/HashingStrategy.java @@ -41,7 +41,7 @@ public interface HashingStrategy { * This method has the following restrictions: *

    *
  • reflexive - {@code equals(a, a)} should return true
  • - *
  • symmetric - {@code equals(a, b)} returns {@code true} iff {@code equals(b, a)} returns + *
  • symmetric - {@code equals(a, b)} returns {@code true} if {@code equals(b, a)} returns * {@code true}
  • *
  • transitive - if {@code equals(a, b)} returns {@code true} and {@code equals(a, c)} returns * {@code true} then {@code equals(b, c)} should also return {@code true}
  • diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index 7ec35b04bc1e..f93d283d43e0 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -1199,7 +1199,7 @@ private SSLEngineResult sslReadErrorResult(int error, int stackError, int bytesC // See https://github.com/netty/netty/issues/3900 if (SSL.bioLengthNonApplication(networkBIO) > 0) { if (handshakeException == null && handshakeState != HandshakeState.FINISHED) { - // we seems to have data left that needs to be transfered and so the user needs + // we seems to have data left that needs to be transferred and so the user needs // call wrap(...). Store the error so we can pick it up later. handshakeException = new SSLHandshakeException(SSL.getErrorString(stackError)); } diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java index 86b01c4de19b..70c8b6b52e03 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java @@ -404,19 +404,19 @@ public Set getRecords(QuestionRecord question) { }); dnsServer2.start(); try { - final Set overridenHostnames = new HashSet(); + final Set overriddenHostnames = new HashSet(); for (String name : DOMAINS) { if (EXCLUSIONS_RESOLVE_A.contains(name)) { continue; } if (PlatformDependent.threadLocalRandom().nextBoolean()) { - overridenHostnames.add(name); + overriddenHostnames.add(name); } } DnsNameResolver resolver = newResolver(false, new DnsServerAddressStreamProvider() { @Override public DnsServerAddressStream nameServerAddressStream(String hostname) { - return overridenHostnames.contains(hostname) ? sequential(dnsServer2.localAddress()).stream() : + return overriddenHostnames.contains(hostname) ? sequential(dnsServer2.localAddress()).stream() : null; } }).build(); @@ -426,7 +426,7 @@ public DnsServerAddressStream nameServerAddressStream(String hostname) { if (resolvedEntry.getValue().isLoopbackAddress()) { continue; } - if (overridenHostnames.contains(resolvedEntry.getKey())) { + if (overriddenHostnames.contains(resolvedEntry.getKey())) { assertEquals("failed to resolve " + resolvedEntry.getKey(), overriddenIP, resolvedEntry.getValue().getHostAddress()); } else { diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelConfig.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelConfig.java index c2b1debe983a..56748e730cd7 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelConfig.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelConfig.java @@ -67,7 +67,7 @@ public boolean setOption(ChannelOption option, T value) { } /** - * If this is {@code true} then the {@link RecvByteBufAllocator.Handle#guess()} will be overriden to always attempt + * If this is {@code true} then the {@link RecvByteBufAllocator.Handle#guess()} will be overridden to always attempt * to read as many bytes as kqueue says are available. */ public KQueueChannelConfig setRcvAllocTransportProvidesGuess(boolean transportProvidesGuess) { @@ -76,7 +76,7 @@ public KQueueChannelConfig setRcvAllocTransportProvidesGuess(boolean transportPr } /** - * If this is {@code true} then the {@link RecvByteBufAllocator.Handle#guess()} will be overriden to always attempt + * If this is {@code true} then the {@link RecvByteBufAllocator.Handle#guess()} will be overridden to always attempt * to read as many bytes as kqueue says are available. */ public boolean getRcvAllocTransportProvidesGuess() { diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelOption.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelOption.java index be5dc4e887aa..e70924789e19 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelOption.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueChannelOption.java @@ -27,7 +27,7 @@ public final class KQueueChannelOption extends UnixChannelOption { public static final ChannelOption SO_ACCEPTFILTER = valueOf(KQueueChannelOption.class, "SO_ACCEPTFILTER"); /** - * If this is {@code true} then the {@link RecvByteBufAllocator.Handle#guess()} will be overriden to always attempt + * If this is {@code true} then the {@link RecvByteBufAllocator.Handle#guess()} will be overridden to always attempt * to read as many bytes as kqueue says are available. */ public static final ChannelOption RCV_ALLOC_TRANSPORT_PROVIDES_GUESS = diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java index d0862748a106..675432a25d96 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java @@ -44,7 +44,7 @@ import static io.netty.channel.unix.Errors.newIOException; /** - * Navite helper methods + * Native helper methods *

    Internal usage only! */ final class Native { diff --git a/transport-native-unix-common/src/main/java/io/netty/channel/unix/DomainSocketReadMode.java b/transport-native-unix-common/src/main/java/io/netty/channel/unix/DomainSocketReadMode.java index 26dea6b196dd..6d99db86cff7 100644 --- a/transport-native-unix-common/src/main/java/io/netty/channel/unix/DomainSocketReadMode.java +++ b/transport-native-unix-common/src/main/java/io/netty/channel/unix/DomainSocketReadMode.java @@ -15,18 +15,20 @@ */ package io.netty.channel.unix; +import io.netty.buffer.ByteBuf; + /** * Different modes of reading from a {@link DomainSocketChannel}. */ public enum DomainSocketReadMode { /** - * Read (@link ByteBuf)s from the {@link DomainSocketChannel}. + * Read {@link ByteBuf}s from the {@link DomainSocketChannel}. */ BYTES, /** - * Read (@link FileDscriptor)s from the {@link DomainSocketChannel}. + * Read {@link FileDescriptor}s from the {@link DomainSocketChannel}. */ FILE_DESCRIPTORS } diff --git a/transport/src/main/java/io/netty/channel/AbstractCoalescingBufferQueue.java b/transport/src/main/java/io/netty/channel/AbstractCoalescingBufferQueue.java index 552793ee92e2..80bf9098d3fb 100644 --- a/transport/src/main/java/io/netty/channel/AbstractCoalescingBufferQueue.java +++ b/transport/src/main/java/io/netty/channel/AbstractCoalescingBufferQueue.java @@ -40,7 +40,7 @@ public abstract class AbstractCoalescingBufferQueue { * * @param channel the {@link Channel} which will have the {@link Channel#isWritable()} reflect the amount of queued * buffers or {@code null} if there is no writability state updated. - * @param initSize theinitial size of the underlying queue. + * @param initSize the initial size of the underlying queue. */ protected AbstractCoalescingBufferQueue(Channel channel, int initSize) { bufAndListenerPairs = new ArrayDeque(initSize); diff --git a/transport/src/main/java/io/netty/channel/DefaultFileRegion.java b/transport/src/main/java/io/netty/channel/DefaultFileRegion.java index 2ccb48586026..0e8d4860c38c 100644 --- a/transport/src/main/java/io/netty/channel/DefaultFileRegion.java +++ b/transport/src/main/java/io/netty/channel/DefaultFileRegion.java @@ -44,7 +44,7 @@ public class DefaultFileRegion extends AbstractReferenceCounted implements FileR /** * Create a new instance * - * @param file the {@link FileChannel} which should be transfered + * @param file the {@link FileChannel} which should be transferred * @param position the position from which the transfer should start * @param count the number of bytes to transfer */ @@ -68,7 +68,7 @@ public DefaultFileRegion(FileChannel file, long position, long count) { * Create a new instance using the given {@link File}. The {@link File} will be opened lazily or * explicitly via {@link #open()}. * - * @param f the {@link File} which should be transfered + * @param f the {@link File} which should be transferred * @param position the position from which the transfer should start * @param count the number of bytes to transfer */ diff --git a/transport/src/main/java/io/netty/channel/FileRegion.java b/transport/src/main/java/io/netty/channel/FileRegion.java index 48fde291317c..8c7128587642 100644 --- a/transport/src/main/java/io/netty/channel/FileRegion.java +++ b/transport/src/main/java/io/netty/channel/FileRegion.java @@ -58,7 +58,7 @@ public interface FileRegion extends ReferenceCounted { long position(); /** - * Returns the bytes which was transfered already. + * Returns the bytes which was transferred already. * * @deprecated Use {@link #transferred()} instead. */ @@ -66,7 +66,7 @@ public interface FileRegion extends ReferenceCounted { long transfered(); /** - * Returns the bytes which was transfered already. + * Returns the bytes which was transferred already. */ long transferred(); diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java index 17aa18f75417..abe9a431d573 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java @@ -123,7 +123,7 @@ public ChannelMetadata metadata() { /** * {@inheritDoc} * - * This can be safetly cast to {@link OioDatagramChannelConfig}. + * This can be safely cast to {@link OioDatagramChannelConfig}. */ @Override // TODO: Change return type to OioDatagramChannelConfig in next major release From 82ec6ba815adcb3ab057ef8a65f6f620a1ad5611 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 14 Jan 2019 08:17:44 +0100 Subject: [PATCH 329/417] Correctly detect and handle CNAME loops. (#8691) Motivation: We do not correctly detect loops when follow CNAMEs and so may try to follow it without any success. Modifications: - Correctly detect CNAME loops - Do not cache CNAME entries which point to itself - Add unit test. Result: Fixes https://github.com/netty/netty/issues/8687. --- .../netty/resolver/dns/DnsResolveContext.java | 21 +++++++-- .../resolver/dns/DnsNameResolverTest.java | 47 +++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java index e75f49c8d46c..847bfb5b9f3c 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java @@ -48,6 +48,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; @@ -651,10 +652,11 @@ private void onExpectedResponse( // Make sure the record is for the questioned domain. if (!recordName.equals(questionName)) { + Map cnamesCopy = new HashMap(cnames); // Even if the record's name is not exactly same, it might be an alias defined in the CNAME records. String resolved = questionName; do { - resolved = cnames.get(resolved); + resolved = cnamesCopy.remove(resolved); if (recordName.equals(resolved)) { break; } @@ -749,8 +751,12 @@ private static Map buildAliasMap(DnsResponse response, DnsCnameC String mapping = domainName.toLowerCase(Locale.US); // Cache the CNAME as well. - cache.cache(hostnameWithDot(name), hostnameWithDot(mapping), r.timeToLive(), loop); - cnames.put(name, mapping); + String nameWithDot = hostnameWithDot(name); + String mappingWithDot = hostnameWithDot(mapping); + if (!nameWithDot.equalsIgnoreCase(mappingWithDot)) { + cache.cache(nameWithDot, mappingWithDot, r.timeToLive(), loop); + cnames.put(name, mapping); + } } return cnames != null? cnames : Collections.emptyMap(); @@ -875,12 +881,21 @@ private DnsServerAddressStream getNameServers(String hostname) { private void followCname(DnsQuestion question, String cname, DnsQueryLifecycleObserver queryLifecycleObserver, Promise> promise) { + Set cnames = null; for (;;) { // Resolve from cnameCache() until there is no more cname entry cached. String mapping = cnameCache().get(hostnameWithDot(cname)); if (mapping == null) { break; } + if (cnames == null) { + // Detect loops. + cnames = new HashSet(2); + } + if (!cnames.add(cname)) { + // Follow CNAME from cache would loop. Lets break here. + break; + } cname = mapping; } diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java index 70c8b6b52e03..1326a7975e8d 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java @@ -96,6 +96,7 @@ import static io.netty.handler.codec.dns.DnsRecordType.CNAME; import static io.netty.resolver.dns.DnsServerAddresses.sequential; import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; @@ -2135,6 +2136,52 @@ public Set getRecords(QuestionRecord question) { } } + @Test + public void testFollowCNAMELoop() throws IOException { + expectedException.expect(UnknownHostException.class); + TestDnsServer dnsServer2 = new TestDnsServer(new RecordStore() { + + @Override + public Set getRecords(QuestionRecord question) { + Set records = new LinkedHashSet(4); + + records.add(new TestDnsServer.TestResourceRecord("x." + question.getDomainName(), + RecordType.A, Collections.singletonMap( + DnsAttribute.IP_ADDRESS.toLowerCase(), "10.0.0.99"))); + records.add(new TestDnsServer.TestResourceRecord( + "cname2.netty.io", RecordType.CNAME, + Collections.singletonMap( + DnsAttribute.DOMAIN_NAME.toLowerCase(), "cname.netty.io"))); + records.add(new TestDnsServer.TestResourceRecord( + "cname.netty.io", RecordType.CNAME, + Collections.singletonMap( + DnsAttribute.DOMAIN_NAME.toLowerCase(), "cname2.netty.io"))); + records.add(new TestDnsServer.TestResourceRecord( + question.getDomainName(), RecordType.CNAME, + Collections.singletonMap( + DnsAttribute.DOMAIN_NAME.toLowerCase(), "cname.netty.io"))); + return records; + } + }); + dnsServer2.start(); + DnsNameResolver resolver = null; + try { + DnsNameResolverBuilder builder = newResolver() + .recursionDesired(false) + .resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY) + .maxQueriesPerResolve(16) + .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())); + + resolver = builder.build(); + resolver.resolveAll("somehost.netty.io").syncUninterruptibly().getNow(); + } finally { + dnsServer2.stop(); + if (resolver != null) { + resolver.close(); + } + } + } + @Test public void testSearchDomainQueryFailureForSingleAddressTypeCompletes() { expectedException.expect(UnknownHostException.class); From 250e2494d9f324bd2771d3c3eee5a6200332f3dc Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 14 Jan 2019 08:19:48 +0100 Subject: [PATCH 330/417] Only call handlerRemoved(...) if handlerAdded(...) was called during adding the handler to the pipeline. (#8684) Motivation: Due a race in DefaultChannelPipeline / AbstractChannelHandlerContext it was possible to have only handlerRemoved(...) called during tearing down the pipeline, even when handlerAdded(...) was never called. We need to ensure we either call both of none to guarantee a proper lifecycle of the handler. Modifications: - Enforce handlerAdded(...) / handlerRemoved(...) semantics / ordering - Add unit test. Result: Fixes https://github.com/netty/netty/issues/8676 / https://github.com/netty/netty/issues/6536 . --- .../AbstractChannelHandlerContext.java | 29 +++++++- .../netty/channel/DefaultChannelPipeline.java | 17 +---- .../channel/DefaultChannelPipelineTest.java | 67 +++++++++++++++++++ 3 files changed, 96 insertions(+), 17 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java index 0a81bd6b0f50..9e599c335d8b 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java @@ -962,14 +962,17 @@ final void setRemoved() { handlerState = REMOVE_COMPLETE; } - final void setAddComplete() { + final boolean setAddComplete() { for (;;) { int oldState = handlerState; + if (oldState == REMOVE_COMPLETE) { + return false; + } // Ensure we never update when the handlerState is REMOVE_COMPLETE already. // oldState is usually ADD_PENDING but can also be REMOVE_COMPLETE when an EventExecutor is used that is not // exposing ordering guarantees. - if (oldState == REMOVE_COMPLETE || HANDLER_STATE_UPDATER.compareAndSet(this, oldState, ADD_COMPLETE)) { - return; + if (HANDLER_STATE_UPDATER.compareAndSet(this, oldState, ADD_COMPLETE)) { + return true; } } } @@ -979,6 +982,26 @@ final void setAddPending() { assert updated; // This should always be true as it MUST be called before setAddComplete() or setRemoved(). } + final void callHandlerAdded() throws Exception { + // We must call setAddComplete before calling handlerAdded. Otherwise if the handlerAdded method generates + // any pipeline events ctx.handler() will miss them because the state will not allow it. + if (setAddComplete()) { + handler().handlerAdded(this); + } + } + + final void callHandlerRemoved() throws Exception { + try { + // Only call handlerRemoved(...) if we called handlerAdded(...) before. + if (handlerState == ADD_COMPLETE) { + handler().handlerRemoved(this); + } + } finally { + // Mark the handler as removed in any case. + setRemoved(); + } + } + /** * Makes best possible effort to detect if {@link ChannelHandler#handlerAdded(ChannelHandlerContext)} was called * yet. If not return {@code false} and if called or could not detect return {@code true}. diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java index 8a6f6dae4665..2155a7cbb379 100644 --- a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java @@ -631,19 +631,12 @@ private static void checkMultiplicity(ChannelHandler handler) { private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) { try { - // We must call setAddComplete before calling handlerAdded. Otherwise if the handlerAdded method generates - // any pipeline events ctx.handler() will miss them because the state will not allow it. - ctx.setAddComplete(); - ctx.handler().handlerAdded(ctx); + ctx.callHandlerAdded(); } catch (Throwable t) { boolean removed = false; try { remove0(ctx); - try { - ctx.handler().handlerRemoved(ctx); - } finally { - ctx.setRemoved(); - } + ctx.callHandlerRemoved(); removed = true; } catch (Throwable t2) { if (logger.isWarnEnabled()) { @@ -666,11 +659,7 @@ private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) { private void callHandlerRemoved0(final AbstractChannelHandlerContext ctx) { // Notify the complete removal. try { - try { - ctx.handler().handlerRemoved(ctx); - } finally { - ctx.setRemoved(); - } + ctx.callHandlerRemoved(); } catch (Throwable t) { fireExceptionCaught(new ChannelPipelineException( ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t)); diff --git a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java index 7cd51b2999f5..31de253b04e7 100644 --- a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java +++ b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java @@ -24,6 +24,7 @@ import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; +import io.netty.channel.local.LocalEventLoopGroup; import io.netty.channel.local.LocalServerChannel; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.oio.OioEventLoopGroup; @@ -1156,6 +1157,72 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { } } + // Test for https://github.com/netty/netty/issues/8676. + @Test + public void testHandlerRemovedOnlyCalledWhenHandlerAddedCalled() throws Exception { + EventLoopGroup group = new DefaultEventLoopGroup(1); + try { + final AtomicReference errorRef = new AtomicReference(); + + // As this only happens via a race we will verify 500 times. This was good enough to have it failed most of + // the time. + for (int i = 0; i < 500; i++) { + + ChannelPipeline pipeline = new LocalChannel().pipeline(); + group.register(pipeline.channel()).sync(); + + final CountDownLatch latch = new CountDownLatch(1); + + pipeline.addLast(new ChannelInboundHandlerAdapter() { + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + // Block just for a bit so we have a chance to trigger the race mentioned in the issue. + latch.await(50, TimeUnit.MILLISECONDS); + } + }); + + // Close the pipeline which will call destroy0(). This will remove each handler in the pipeline and + // should call handlerRemoved(...) if and only if handlerAdded(...) was called for the handler before. + pipeline.close(); + + pipeline.addLast(new ChannelInboundHandlerAdapter() { + private boolean handerAddedCalled; + + @Override + public void handlerAdded(ChannelHandlerContext ctx) { + handerAddedCalled = true; + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) { + if (!handerAddedCalled) { + errorRef.set(new AssertionError( + "handlerRemoved(...) called without handlerAdded(...) before")); + } + } + }); + + latch.countDown(); + + pipeline.channel().closeFuture().syncUninterruptibly(); + + // Schedule something on the EventLoop to ensure all other scheduled tasks had a chance to complete. + pipeline.channel().eventLoop().submit(new Runnable() { + @Override + public void run() { + // NOOP + } + }).syncUninterruptibly(); + Error error = errorRef.get(); + if (error != null) { + throw error; + } + } + } finally { + group.shutdownGracefully(); + } + } + @Test(timeout = 5000) public void handlerAddedStateUpdatedBeforeHandlerAddedDoneForceEventLoop() throws InterruptedException { handlerAddedStateUpdatedBeforeHandlerAddedDone(true); From 4155bc08f04399611ae413b40c1d6042ba736195 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 14 Jan 2019 08:25:45 +0100 Subject: [PATCH 331/417] Correctly buffer multiple outbound streams if needed. (#8694) Motivation: In Http2FrameCodec we made the incorrect assumption that we can only have 1 buffered outboundstream as maximum. This is not correct and we need to account for multiple buffered streams. Modifications: - Use a map to allow buffer multiple streams - Add unit test. Result: Fixes https://github.com/netty/netty/issues/8692. --- .../handler/codec/http2/Http2FrameCodec.java | 33 ++++++++------ .../codec/http2/Http2FrameCodecTest.java | 43 +++++++++++++++++++ 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java index b9075144f3e8..cf756cab25c7 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java @@ -29,6 +29,8 @@ import io.netty.handler.codec.http2.StreamBufferingEncoder.Http2GoAwayException; import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCounted; +import io.netty.util.collection.IntObjectHashMap; +import io.netty.util.collection.IntObjectMap; import io.netty.util.internal.UnstableApi; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -152,7 +154,8 @@ public class Http2FrameCodec extends Http2ConnectionHandler { /** Number of buffered streams if the {@link StreamBufferingEncoder} is used. **/ private int numBufferedStreams; - private DefaultHttp2FrameStream frameStreamToInitialize; + private final IntObjectMap frameStreamToInitializeMap = + new IntObjectHashMap(8); Http2FrameCodec(Http2ConnectionEncoder encoder, Http2ConnectionDecoder decoder, Http2Settings initialSettings) { super(decoder, encoder, initialSettings); @@ -358,12 +361,17 @@ private void writeHeadersFrame( } stream.id = streamId; - // TODO: This depends on the fact that the connection based API will create Http2Stream objects - // synchronously. We should investigate how to refactor this later on when we consolidate some layers. - assert frameStreamToInitialize == null; - frameStreamToInitialize = stream; + // Use a Map to store all pending streams as we may have multiple. This is needed as if we would store the + // stream in a field directly we may override the stored field before onStreamAdded(...) was called + // and so not correctly set the property for the buffered stream. + // + // See https://github.com/netty/netty/issues/8692 + Object old = frameStreamToInitializeMap.put(streamId, stream); + + // We should not re-use ids. + assert old == null; - // TODO(buchgr): Once Http2Stream2 and Http2Stream are merged this is no longer necessary. + // TODO(buchgr): Once Http2FrameStream and Http2Stream are merged this is no longer necessary. final ChannelPromise writePromise = ctx.newPromise(); encoder().writeHeaders(ctx, streamId, headersFrame.headers(), headersFrame.padding(), @@ -399,7 +407,7 @@ private void onStreamActive0(Http2Stream stream) { return; } - DefaultHttp2FrameStream stream2 = newStream().setStreamAndProperty(streamKey, stream); + Http2FrameStream stream2 = newStream().setStreamAndProperty(streamKey, stream); onHttp2StreamStateChanged(ctx, stream2); } @@ -407,9 +415,10 @@ private final class ConnectionListener extends Http2ConnectionAdapter { @Override public void onStreamAdded(Http2Stream stream) { - if (frameStreamToInitialize != null && stream.id() == frameStreamToInitialize.id()) { - frameStreamToInitialize.setStreamAndProperty(streamKey, stream); - frameStreamToInitialize = null; + DefaultHttp2FrameStream frameStream = frameStreamToInitializeMap.remove(stream.id()); + + if (frameStream != null) { + frameStream.setStreamAndProperty(streamKey, stream); } } @@ -420,7 +429,7 @@ public void onStreamActive(Http2Stream stream) { @Override public void onStreamClosed(Http2Stream stream) { - DefaultHttp2FrameStream stream2 = stream.getProperty(streamKey); + Http2FrameStream stream2 = stream.getProperty(streamKey); if (stream2 != null) { onHttp2StreamStateChanged(ctx, stream2); } @@ -428,7 +437,7 @@ public void onStreamClosed(Http2Stream stream) { @Override public void onStreamHalfClosed(Http2Stream stream) { - DefaultHttp2FrameStream stream2 = stream.getProperty(streamKey); + Http2FrameStream stream2 = stream.getProperty(streamKey); if (stream2 != null) { onHttp2StreamStateChanged(ctx, stream2); } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java index a3ccf038d017..27d13cf50b32 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java @@ -619,6 +619,49 @@ public void newOutboundStreamsShouldBeBuffered() throws Exception { assertTrue(promise2.syncUninterruptibly().isSuccess()); } + @Test + public void multipleNewOutboundStreamsShouldBeBuffered() throws Exception { + // We use a limit of 1 and then increase it step by step. + setUp(Http2FrameCodecBuilder.forServer().encoderEnforceMaxConcurrentStreams(true), + new Http2Settings().maxConcurrentStreams(1)); + + Http2FrameStream stream1 = frameCodec.newStream(); + Http2FrameStream stream2 = frameCodec.newStream(); + Http2FrameStream stream3 = frameCodec.newStream(); + + ChannelPromise promise1 = channel.newPromise(); + ChannelPromise promise2 = channel.newPromise(); + ChannelPromise promise3 = channel.newPromise(); + + channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1), promise1); + channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2), promise2); + channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream3), promise3); + + assertTrue(isStreamIdValid(stream1.id())); + channel.runPendingTasks(); + assertTrue(isStreamIdValid(stream2.id())); + + assertTrue(promise1.syncUninterruptibly().isSuccess()); + assertFalse(promise2.isDone()); + assertFalse(promise3.isDone()); + + // Increase concurrent streams limit to 2 + frameInboundWriter.writeInboundSettings(new Http2Settings().maxConcurrentStreams(2)); + channel.flush(); + + // As we increased the limit to 2 we should have also succeed the second frame. + assertTrue(promise2.syncUninterruptibly().isSuccess()); + assertFalse(promise3.isDone()); + + frameInboundWriter.writeInboundSettings(new Http2Settings().maxConcurrentStreams(3)); + channel.flush(); + + // With the max streams of 3 all streams should be succeed now. + assertTrue(promise3.syncUninterruptibly().isSuccess()); + + assertFalse(channel.finishAndReleaseAll()); + } + @Test public void streamIdentifiersExhausted() throws Http2Exception { int maxServerStreamId = Integer.MAX_VALUE - 1; From 9fb0765891287a00154627ed79f7d66e2100c16a Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 14 Jan 2019 13:33:37 +0100 Subject: [PATCH 332/417] Use OpenJDK 12 EA 27 when running CI jobs for JDK 12. (#8715) Motivation: A new EA release was done for OpenJDK12. Modifications: Use OpenJDK12 EA 27 when running CI jobs for JDK 12. Result: Test against latest OpenJDK 12 EA build. --- docker/docker-compose.centos-6.112.yaml | 2 +- docker/docker-compose.centos-7.112.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.centos-6.112.yaml b/docker/docker-compose.centos-6.112.yaml index cc7f3bb5697a..08b54114a9bd 100644 --- a/docker/docker-compose.centos-6.112.yaml +++ b/docker/docker-compose.centos-6.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "openjdk@1.12.0-24" + java_version : "openjdk@1.12.0-27" test: image: netty:centos-6-1.12 diff --git a/docker/docker-compose.centos-7.112.yaml b/docker/docker-compose.centos-7.112.yaml index 1c009cb94b3c..b85a1d6e5ad0 100644 --- a/docker/docker-compose.centos-7.112.yaml +++ b/docker/docker-compose.centos-7.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "openjdk@1.12.0-24" + java_version : "openjdk@1.12.0-27" test: image: netty:centos-7-1.12 From 1b9cdc1f63d1a2b2ef51ca11568128ddae6323ba Mon Sep 17 00:00:00 2001 From: Derek Lewis Date: Mon, 14 Jan 2019 11:08:49 -0800 Subject: [PATCH 333/417] Updating `ByteBuf` Javadocs to represent actual behaviour. (#8709) Motivation: The javadocs stating `IndexOutOfBoundsException` is thrown were different from what `ByteBuf` actually did. We want to ensure the Javadocs represent reality. Modifications: Updated javadocs on `write*`, `ensureWriteable`, `capacity`, and `maxCapacity` methods. Results: Javadocs more closely match actual behaviour. --- .../main/java/io/netty/buffer/ByteBuf.java | 168 ++++++++---------- 1 file changed, 74 insertions(+), 94 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/ByteBuf.java b/buffer/src/main/java/io/netty/buffer/ByteBuf.java index c04340a0d15e..2ac7e988c6bc 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBuf.java @@ -258,14 +258,14 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable { * capacity, the content of this buffer is truncated. If the {@code newCapacity} is greater * than the current capacity, the buffer is appended with unspecified data whose length is * {@code (newCapacity - currentCapacity)}. + * + * @throws IllegalArgumentException if the {@code newCapacity} is greater than {@link #maxCapacity()} */ public abstract ByteBuf capacity(int newCapacity); /** - * Returns the maximum allowed capacity of this buffer. If a user attempts to increase the - * capacity of this buffer beyond the maximum capacity using {@link #capacity(int)} or - * {@link #ensureWritable(int)}, those methods will raise an - * {@link IllegalArgumentException}. + * Returns the maximum allowed capacity of this buffer. This value provides an upper + * bound on {@link #capacity()}. */ public abstract int maxCapacity(); @@ -513,22 +513,23 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable { public abstract ByteBuf discardSomeReadBytes(); /** - * Makes sure the number of {@linkplain #writableBytes() the writable bytes} - * is equal to or greater than the specified value. If there is enough - * writable bytes in this buffer, this method returns with no side effect. - * Otherwise, it raises an {@link IllegalArgumentException}. + * Expands the buffer {@link #capacity()} to make sure the number of + * {@linkplain #writableBytes() writable bytes} is equal to or greater than the + * specified value. If there are enough writable bytes in this buffer, this method + * returns with no side effect. * * @param minWritableBytes * the expected minimum number of writable bytes * @throws IndexOutOfBoundsException - * if {@link #writerIndex()} + {@code minWritableBytes} > {@link #maxCapacity()} + * if {@link #writerIndex()} + {@code minWritableBytes} > {@link #maxCapacity()}. + * @see #capacity(int) */ public abstract ByteBuf ensureWritable(int minWritableBytes); /** - * Tries to make sure the number of {@linkplain #writableBytes() the writable bytes} - * is equal to or greater than the specified value. Unlike {@link #ensureWritable(int)}, - * this method does not raise an exception but returns a code. + * Expands the buffer {@link #capacity()} to make sure the number of + * {@linkplain #writableBytes() writable bytes} is equal to or greater than the + * specified value. Unlike {@link #ensureWritable(int)}, this method returns a status code. * * @param minWritableBytes * the expected minimum number of writable bytes @@ -1756,9 +1757,8 @@ public double readDoubleLE() { /** * Sets the specified boolean at the current {@code writerIndex} * and increases the {@code writerIndex} by {@code 1} in this buffer. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 1} + * If {@code this.writableBytes} is less than {@code 1}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeBoolean(boolean value); @@ -1766,9 +1766,8 @@ public double readDoubleLE() { * Sets the specified byte at the current {@code writerIndex} * and increases the {@code writerIndex} by {@code 1} in this buffer. * The 24 high-order bits of the specified value are ignored. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 1} + * If {@code this.writableBytes} is less than {@code 1}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeByte(int value); @@ -1776,9 +1775,8 @@ public double readDoubleLE() { * Sets the specified 16-bit short integer at the current * {@code writerIndex} and increases the {@code writerIndex} by {@code 2} * in this buffer. The 16 high-order bits of the specified value are ignored. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 2} + * If {@code this.writableBytes} is less than {@code 2}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeShort(int value); @@ -1787,9 +1785,8 @@ public double readDoubleLE() { * Order at the current {@code writerIndex} and increases the * {@code writerIndex} by {@code 2} in this buffer. * The 16 high-order bits of the specified value are ignored. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 2} + * If {@code this.writableBytes} is less than {@code 2}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeShortLE(int value); @@ -1797,9 +1794,8 @@ public double readDoubleLE() { * Sets the specified 24-bit medium integer at the current * {@code writerIndex} and increases the {@code writerIndex} by {@code 3} * in this buffer. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 3} + * If {@code this.writableBytes} is less than {@code 3}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeMedium(int value); @@ -1808,18 +1804,16 @@ public double readDoubleLE() { * {@code writerIndex} in the Little Endian Byte Order and * increases the {@code writerIndex} by {@code 3} in this * buffer. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 3} + * If {@code this.writableBytes} is less than {@code 3}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeMediumLE(int value); /** * Sets the specified 32-bit integer at the current {@code writerIndex} * and increases the {@code writerIndex} by {@code 4} in this buffer. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 4} + * If {@code this.writableBytes} is less than {@code 4}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeInt(int value); @@ -1827,9 +1821,8 @@ public double readDoubleLE() { * Sets the specified 32-bit integer at the current {@code writerIndex} * in the Little Endian Byte Order and increases the {@code writerIndex} * by {@code 4} in this buffer. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 4} + * If {@code this.writableBytes} is less than {@code 4}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeIntLE(int value); @@ -1837,9 +1830,8 @@ public double readDoubleLE() { * Sets the specified 64-bit long integer at the current * {@code writerIndex} and increases the {@code writerIndex} by {@code 8} * in this buffer. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 8} + * If {@code this.writableBytes} is less than {@code 8}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeLong(long value); @@ -1848,9 +1840,8 @@ public double readDoubleLE() { * {@code writerIndex} in the Little Endian Byte Order and * increases the {@code writerIndex} by {@code 8} * in this buffer. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 8} + * If {@code this.writableBytes} is less than {@code 8}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeLongLE(long value); @@ -1858,9 +1849,8 @@ public double readDoubleLE() { * Sets the specified 2-byte UTF-16 character at the current * {@code writerIndex} and increases the {@code writerIndex} by {@code 2} * in this buffer. The 16 high-order bits of the specified value are ignored. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 2} + * If {@code this.writableBytes} is less than {@code 2}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeChar(int value); @@ -1868,9 +1858,8 @@ public double readDoubleLE() { * Sets the specified 32-bit floating point number at the current * {@code writerIndex} and increases the {@code writerIndex} by {@code 4} * in this buffer. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 4} + * If {@code this.writableBytes} is less than {@code 4}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeFloat(float value); @@ -1878,9 +1867,8 @@ public double readDoubleLE() { * Sets the specified 32-bit floating point number at the current * {@code writerIndex} in Little Endian Byte Order and increases * the {@code writerIndex} by {@code 4} in this buffer. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 4} + * If {@code this.writableBytes} is less than {@code 4}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public ByteBuf writeFloatLE(float value) { return writeIntLE(Float.floatToRawIntBits(value)); @@ -1890,9 +1878,8 @@ public ByteBuf writeFloatLE(float value) { * Sets the specified 64-bit floating point number at the current * {@code writerIndex} and increases the {@code writerIndex} by {@code 8} * in this buffer. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 8} + * If {@code this.writableBytes} is less than {@code 8}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeDouble(double value); @@ -1900,9 +1887,8 @@ public ByteBuf writeFloatLE(float value) { * Sets the specified 64-bit floating point number at the current * {@code writerIndex} in Little Endian Byte Order and increases * the {@code writerIndex} by {@code 8} in this buffer. - * - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is less than {@code 8} + * If {@code this.writableBytes} is less than {@code 8}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public ByteBuf writeDoubleLE(double value) { return writeLongLE(Double.doubleToRawLongBits(value)); @@ -1917,10 +1903,9 @@ public ByteBuf writeDoubleLE(double value) { * increases the {@code readerIndex} of the source buffer by the number of * the transferred bytes while {@link #writeBytes(ByteBuf, int, int)} * does not. - * - * @throws IndexOutOfBoundsException - * if {@code src.readableBytes} is greater than - * {@code this.writableBytes} + * If {@code this.writableBytes} is less than {@code src.readableBytes}, + * {@link #ensureWritable(int)} will be called in an attempt to expand + * capacity to accommodate. */ public abstract ByteBuf writeBytes(ByteBuf src); @@ -1932,12 +1917,11 @@ public ByteBuf writeDoubleLE(double value) { * except that this method increases the {@code readerIndex} of the source * buffer by the number of the transferred bytes (= {@code length}) while * {@link #writeBytes(ByteBuf, int, int)} does not. + * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. * * @param length the number of bytes to transfer - * - * @throws IndexOutOfBoundsException - * if {@code length} is greater than {@code this.writableBytes} or - * if {@code length} is greater then {@code src.readableBytes} + * @throws IndexOutOfBoundsException if {@code length} is greater then {@code src.readableBytes} */ public abstract ByteBuf writeBytes(ByteBuf src, int length); @@ -1945,15 +1929,15 @@ public ByteBuf writeDoubleLE(double value) { * Transfers the specified source buffer's data to this buffer starting at * the current {@code writerIndex} and increases the {@code writerIndex} * by the number of the transferred bytes (= {@code length}). + * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. * * @param srcIndex the first index of the source * @param length the number of bytes to transfer * * @throws IndexOutOfBoundsException - * if the specified {@code srcIndex} is less than {@code 0}, - * if {@code srcIndex + length} is greater than - * {@code src.capacity}, or - * if {@code length} is greater than {@code this.writableBytes} + * if the specified {@code srcIndex} is less than {@code 0}, or + * if {@code srcIndex + length} is greater than {@code src.capacity} */ public abstract ByteBuf writeBytes(ByteBuf src, int srcIndex, int length); @@ -1961,9 +1945,8 @@ public ByteBuf writeDoubleLE(double value) { * Transfers the specified source array's data to this buffer starting at * the current {@code writerIndex} and increases the {@code writerIndex} * by the number of the transferred bytes (= {@code src.length}). - * - * @throws IndexOutOfBoundsException - * if {@code src.length} is greater than {@code this.writableBytes} + * If {@code this.writableBytes} is less than {@code src.length}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. */ public abstract ByteBuf writeBytes(byte[] src); @@ -1971,15 +1954,15 @@ public ByteBuf writeDoubleLE(double value) { * Transfers the specified source array's data to this buffer starting at * the current {@code writerIndex} and increases the {@code writerIndex} * by the number of the transferred bytes (= {@code length}). + * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. * * @param srcIndex the first index of the source * @param length the number of bytes to transfer * * @throws IndexOutOfBoundsException - * if the specified {@code srcIndex} is less than {@code 0}, - * if {@code srcIndex + length} is greater than - * {@code src.length}, or - * if {@code length} is greater than {@code this.writableBytes} + * if the specified {@code srcIndex} is less than {@code 0}, or + * if {@code srcIndex + length} is greater than {@code src.length} */ public abstract ByteBuf writeBytes(byte[] src, int srcIndex, int length); @@ -1988,10 +1971,9 @@ public ByteBuf writeDoubleLE(double value) { * the current {@code writerIndex} until the source buffer's position * reaches its limit, and increases the {@code writerIndex} by the * number of the transferred bytes. - * - * @throws IndexOutOfBoundsException - * if {@code src.remaining()} is greater than - * {@code this.writableBytes} + * If {@code this.writableBytes} is less than {@code src.remaining()}, + * {@link #ensureWritable(int)} will be called in an attempt to expand + * capacity to accommodate. */ public abstract ByteBuf writeBytes(ByteBuffer src); @@ -1999,29 +1981,28 @@ public ByteBuf writeDoubleLE(double value) { * Transfers the content of the specified stream to this buffer * starting at the current {@code writerIndex} and increases the * {@code writerIndex} by the number of the transferred bytes. + * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. * * @param length the number of bytes to transfer * * @return the actual number of bytes read in from the specified stream * - * @throws IndexOutOfBoundsException - * if {@code length} is greater than {@code this.writableBytes} - * @throws IOException - * if the specified stream threw an exception during I/O + * @throws IOException if the specified stream threw an exception during I/O */ - public abstract int writeBytes(InputStream in, int length) throws IOException; + public abstract int writeBytes(InputStream in, int length) throws IOException; /** * Transfers the content of the specified channel to this buffer * starting at the current {@code writerIndex} and increases the * {@code writerIndex} by the number of the transferred bytes. + * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. * * @param length the maximum number of bytes to transfer * * @return the actual number of bytes read in from the specified channel * - * @throws IndexOutOfBoundsException - * if {@code length} is greater than {@code this.writableBytes} * @throws IOException * if the specified channel threw an exception during I/O */ @@ -2032,14 +2013,14 @@ public ByteBuf writeDoubleLE(double value) { * to this buffer starting at the current {@code writerIndex} and increases the * {@code writerIndex} by the number of the transferred bytes. * This method does not modify the channel's position. + * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. * * @param position the file position at which the transfer is to begin * @param length the maximum number of bytes to transfer * * @return the actual number of bytes read in from the specified channel * - * @throws IndexOutOfBoundsException - * if {@code length} is greater than {@code this.writableBytes} * @throws IOException * if the specified channel threw an exception during I/O */ @@ -2049,11 +2030,10 @@ public ByteBuf writeDoubleLE(double value) { * Fills this buffer with NUL (0x00) starting at the current * {@code writerIndex} and increases the {@code writerIndex} by the * specified {@code length}. + * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)} + * will be called in an attempt to expand capacity to accommodate. * * @param length the number of NULs to write to the buffer - * - * @throws IndexOutOfBoundsException - * if {@code length} is greater than {@code this.writableBytes} */ public abstract ByteBuf writeZero(int length); @@ -2061,12 +2041,12 @@ public ByteBuf writeDoubleLE(double value) { * Writes the specified {@link CharSequence} at the current {@code writerIndex} and increases * the {@code writerIndex} by the written bytes. * in this buffer. + * If {@code this.writableBytes} is not large enough to write the whole sequence, + * {@link #ensureWritable(int)} will be called in an attempt to expand capacity to accommodate. * * @param sequence to write * @param charset that should be used * @return the written number of bytes - * @throws IndexOutOfBoundsException - * if {@code this.writableBytes} is not large enough to write the whole sequence */ public abstract int writeCharSequence(CharSequence sequence, Charset charset); From 4ac5264f0e3439f5da4b4a8337e7c56ba1937c55 Mon Sep 17 00:00:00 2001 From: Derek Lewis Date: Mon, 14 Jan 2019 23:33:29 -0800 Subject: [PATCH 334/417] Remove unnecessary loop variable from `AsciiString`. (#8711) Motivation: Incrementing two variables in sync is not necessary when only one will do. Modifications: - Remove `j` from `for` loop and replace with `i`. - Add more unit testing scenarios to cover changed code. Results: Unnecessary variable removed. --- common/src/main/java/io/netty/util/AsciiString.java | 4 ++-- .../src/test/java/io/netty/util/AsciiStringCharacterTest.java | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/io/netty/util/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java index 3d3e1fbc21d2..f53746960a89 100644 --- a/common/src/main/java/io/netty/util/AsciiString.java +++ b/common/src/main/java/io/netty/util/AsciiString.java @@ -1452,8 +1452,8 @@ public static boolean contentEqualsIgnoreCase(CharSequence a, CharSequence b) { if (a.length() != b.length()) { return false; } - for (int i = 0, j = 0; i < a.length(); ++i, ++j) { - if (!equalsIgnoreCase(a.charAt(i), b.charAt(j))) { + for (int i = 0; i < a.length(); ++i) { + if (!equalsIgnoreCase(a.charAt(i), b.charAt(i))) { return false; } } diff --git a/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java b/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java index cdd962d3546e..c2a835df660a 100644 --- a/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java +++ b/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java @@ -240,6 +240,9 @@ public void testEqualsIgnoreCase() { assertThat(AsciiString.contentEqualsIgnoreCase(null, "foo"), is(false)); assertThat(AsciiString.contentEqualsIgnoreCase("bar", null), is(false)); assertThat(AsciiString.contentEqualsIgnoreCase("FoO", "fOo"), is(true)); + assertThat(AsciiString.contentEqualsIgnoreCase("FoO", "bar"), is(false)); + assertThat(AsciiString.contentEqualsIgnoreCase("Foo", "foobar"), is(false)); + assertThat(AsciiString.contentEqualsIgnoreCase("foobar", "Foo"), is(false)); // Test variations (Ascii + String, Ascii + Ascii, String + Ascii) assertThat(AsciiString.contentEqualsIgnoreCase(new AsciiString("FoO"), "fOo"), is(true)); From c424599593b0feee6bb76735cd60ad8f151d21fb Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 15 Jan 2019 08:38:13 +0100 Subject: [PATCH 335/417] Access the Constructor of the Channel in the constructor of ReflectiveChannelFactory. (#8718) Motivation: We should access the Constructor of the passed in class in the Constructor of ReflectiveChannelFactory only to reduce the overhead but also fail-fast. Modifications: Access the Constructor early. Result: Fails fast and less performance overhead. --- .../channel/ReflectiveChannelFactory.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/ReflectiveChannelFactory.java b/transport/src/main/java/io/netty/channel/ReflectiveChannelFactory.java index 502d15f17327..677c1c071d21 100644 --- a/transport/src/main/java/io/netty/channel/ReflectiveChannelFactory.java +++ b/transport/src/main/java/io/netty/channel/ReflectiveChannelFactory.java @@ -16,33 +16,40 @@ package io.netty.channel; +import io.netty.util.internal.ObjectUtil; import io.netty.util.internal.StringUtil; +import java.lang.reflect.Constructor; + /** * A {@link ChannelFactory} that instantiates a new {@link Channel} by invoking its default constructor reflectively. */ public class ReflectiveChannelFactory implements ChannelFactory { - private final Class clazz; + private final Constructor constructor; public ReflectiveChannelFactory(Class clazz) { - if (clazz == null) { - throw new NullPointerException("clazz"); + ObjectUtil.checkNotNull(clazz, "clazz"); + try { + this.constructor = clazz.getConstructor(); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) + + " does not have a public non-arg constructor", e); } - this.clazz = clazz; } @Override public T newChannel() { try { - return clazz.getConstructor().newInstance(); + return constructor.newInstance(); } catch (Throwable t) { - throw new ChannelException("Unable to create Channel from class " + clazz, t); + throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t); } } @Override public String toString() { - return StringUtil.simpleClassName(clazz) + ".class"; + return StringUtil.simpleClassName(ReflectiveChannelFactory.class) + + '(' + StringUtil.simpleClassName(constructor.getDeclaringClass()) + ".class)"; } } From 53d711bdc71ecd68cb1028c841b11b584f5c8ca6 Mon Sep 17 00:00:00 2001 From: kezhenxu94 Date: Wed, 16 Jan 2019 17:56:07 +0800 Subject: [PATCH 336/417] extract duplicate code into method (#8720) Motivation: Clean up code to increase readability. Modification: Extract duplicate code blocks into method. Result: Less code duplication --- .../concurrent/SingleThreadEventExecutor.java | 48 +++++++++---------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java index 56fd442b6667..ab2a914c694b 100644 --- a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java +++ b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java @@ -595,19 +595,8 @@ public Future shutdownGracefully(long quietPeriod, long timeout, TimeUnit uni gracefulShutdownQuietPeriod = unit.toNanos(quietPeriod); gracefulShutdownTimeout = unit.toNanos(timeout); - if (oldState == ST_NOT_STARTED) { - try { - doStartThread(); - } catch (Throwable cause) { - STATE_UPDATER.set(this, ST_TERMINATED); - terminationFuture.tryFailure(cause); - - if (!(cause instanceof Exception)) { - // Also rethrow as it may be an OOME for example - PlatformDependent.throwException(cause); - } - return terminationFuture; - } + if (ensureThreadStarted(oldState)) { + return terminationFuture; } if (wakeup) { @@ -658,19 +647,8 @@ public void shutdown() { } } - if (oldState == ST_NOT_STARTED) { - try { - doStartThread(); - } catch (Throwable cause) { - STATE_UPDATER.set(this, ST_TERMINATED); - terminationFuture.tryFailure(cause); - - if (!(cause instanceof Exception)) { - // Also rethrow as it may be an OOME for example - PlatformDependent.throwException(cause); - } - return; - } + if (ensureThreadStarted(oldState)) { + return; } if (wakeup) { @@ -893,6 +871,24 @@ private void startThread() { } } + private boolean ensureThreadStarted(int oldState) { + if (oldState == ST_NOT_STARTED) { + try { + doStartThread(); + } catch (Throwable cause) { + STATE_UPDATER.set(this, ST_TERMINATED); + terminationFuture.tryFailure(cause); + + if (!(cause instanceof Exception)) { + // Also rethrow as it may be an OOME for example + PlatformDependent.throwException(cause); + } + return true; + } + } + return false; + } + private void doStartThread() { assert thread == null; executor.execute(new Runnable() { From 165912365a8a375c32f2747ca0836fd604f4ad17 Mon Sep 17 00:00:00 2001 From: Dmitriy Dumanskiy Date: Wed, 16 Jan 2019 12:00:25 +0200 Subject: [PATCH 337/417] Clenaup: simplify EpollEventLoop.closeAll() (#8719) Motivation: Avoid unnecessary iteration and `ArrayList` allocation. Modification: ``` for (AbstractEpollChannel channel: channels.values()) { array.add(channel); } ``` replaced with `array.addAll(channels.values())` and ``` Collection array = new ArrayList(channels.size()); array.addAll(channels.values()) ``` replaced with: `AbstractEpollChannel[] localChannels = channels.values().toArray(new AbstractEpollChannel[0]);` Result: Simpler code in `EpollEventLoop.closeAll();` --- .../java/io/netty/channel/epoll/EpollEventLoop.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index 33adf8627963..3420d67bc898 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -32,8 +32,6 @@ import io.netty.util.internal.logging.InternalLoggerFactory; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; import java.util.Queue; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; @@ -379,13 +377,9 @@ private void closeAll() { } // Using the intermediate collection to prevent ConcurrentModificationException. // In the `close()` method, the channel is deleted from `channels` map. - Collection array = new ArrayList(channels.size()); + AbstractEpollChannel[] localChannels = channels.values().toArray(new AbstractEpollChannel[0]); - for (AbstractEpollChannel channel: channels.values()) { - array.add(channel); - } - - for (AbstractEpollChannel ch: array) { + for (AbstractEpollChannel ch : localChannels) { ch.unsafe().close(ch.unsafe().voidPromise()); } } From 7988cfec0a32a44a879b60e65ad4a9cabfd3c4d7 Mon Sep 17 00:00:00 2001 From: Oleksii Kachaiev Date: Wed, 16 Jan 2019 12:07:59 +0200 Subject: [PATCH 338/417] Correctly propagate write failures from ChunkedWriteHandler (#8716) Motivation: ChunkedWriteHandler should report write operation as failed in case *any* chunked was not written. Right now this is not true for the last chunk. Modifications: * Check if the appropriate write operation was succesfull when reporting the last chunk * Skip writing chunks if the write operation was already marked as "done" * Test cases to cover write failures when dealing with chunked input Result: Fix https://github.com/netty/netty/issues/8700 --- .../handler/stream/ChunkedWriteHandler.java | 25 +- .../stream/ChunkedWriteHandlerTest.java | 281 +++++++++++++++++- 2 files changed, 297 insertions(+), 9 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java index b4805d98ef0c..f39328dff7a2 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java @@ -209,6 +209,21 @@ private void doFlush(final ChannelHandlerContext ctx) { if (currentWrite == null) { break; } + + if (currentWrite.promise.isDone()) { + // This might happen e.g. in the case when a write operation + // failed, but there're still unconsumed chunks left. + // Most chunked input sources would stop generating chunks + // and report end of input, but this doesn't work with any + // source wrapped in HttpChunkedInput. + // Note, that we're not trying to release the message/chunks + // as this had to be done already by someone who resolved the + // promise (using ChunkedInput.close method). + // See https://github.com/netty/netty/issues/8700. + this.currentWrite = null; + continue; + } + final PendingWrite currentWrite = this.currentWrite; final Object pendingMessage = currentWrite.msg; @@ -264,9 +279,13 @@ private void doFlush(final ChannelHandlerContext ctx) { f.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { - currentWrite.progress(chunks.progress(), chunks.length()); - currentWrite.success(chunks.length()); - closeInput(chunks); + if (!future.isSuccess()) { + closeInput(chunks); + currentWrite.fail(future.cause()); + } else { + currentWrite.progress(chunks.progress(), chunks.length()); + currentWrite.success(chunks.length()); + } } }); } else if (channel.isWritable()) { diff --git a/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java b/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java index 66b69516fc0d..5b03048ba697 100644 --- a/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java @@ -21,8 +21,11 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.CharsetUtil; +import io.netty.util.ReferenceCountUtil; import org.junit.Test; import java.io.ByteArrayInputStream; @@ -31,10 +34,9 @@ import java.io.IOException; import java.nio.channels.Channels; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class ChunkedWriteHandlerTest { private static final byte[] BYTES = new byte[1024 * 64]; @@ -162,8 +164,7 @@ public void operationComplete(ChannelFuture future) throws Exception { EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); ch.writeAndFlush(input).addListener(listener).syncUninterruptibly(); - ch.checkException(); - ch.finish(); + assertTrue(ch.finish()); // the listener should have been notified assertTrue(listenerNotified.get()); @@ -220,13 +221,218 @@ public long progress() { EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); ch.writeAndFlush(input).syncUninterruptibly(); - ch.checkException(); assertTrue(ch.finish()); assertEquals(0, ch.readOutbound()); assertNull(ch.readOutbound()); } + @Test + public void testWriteFailureChunkedStream() throws IOException { + checkFirstFailed(new ChunkedStream(new ByteArrayInputStream(BYTES))); + } + + @Test + public void testWriteFailureChunkedNioStream() throws IOException { + checkFirstFailed(new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES)))); + } + + @Test + public void testWriteFailureChunkedFile() throws IOException { + checkFirstFailed(new ChunkedFile(TMP)); + } + + @Test + public void testWriteFailureChunkedNioFile() throws IOException { + checkFirstFailed(new ChunkedNioFile(TMP)); + } + + @Test + public void testWriteFailureUnchunkedData() throws IOException { + checkFirstFailed(Unpooled.wrappedBuffer(BYTES)); + } + + @Test + public void testSkipAfterFailedChunkedStream() throws IOException { + checkSkipFailed(new ChunkedStream(new ByteArrayInputStream(BYTES)), + new ChunkedStream(new ByteArrayInputStream(BYTES))); + } + + @Test + public void testSkipAfterFailedChunkedNioStream() throws IOException { + checkSkipFailed(new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES))), + new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES)))); + } + + @Test + public void testSkipAfterFailedChunkedFile() throws IOException { + checkSkipFailed(new ChunkedFile(TMP), new ChunkedFile(TMP)); + } + + @Test + public void testSkipAfterFailedChunkedNioFile() throws IOException { + checkSkipFailed(new ChunkedNioFile(TMP), new ChunkedFile(TMP)); + } + + // See https://github.com/netty/netty/issues/8700. + @Test + public void testFailureWhenLastChunkFailed() throws IOException { + ChannelOutboundHandlerAdapter failLast = new ChannelOutboundHandlerAdapter() { + private int passedWrites; + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { + if (++this.passedWrites < 4) { + ctx.write(msg, promise); + } else { + ReferenceCountUtil.release(msg); + promise.tryFailure(new RuntimeException()); + } + } + }; + + EmbeddedChannel ch = new EmbeddedChannel(failLast, new ChunkedWriteHandler()); + ChannelFuture r = ch.writeAndFlush(new ChunkedFile(TMP, 1024 * 16)); // 4 chunks + assertTrue(ch.finish()); + + assertFalse(r.isSuccess()); + assertTrue(r.cause() instanceof RuntimeException); + + // 3 out of 4 chunks were already written + int read = 0; + for (;;) { + ByteBuf buffer = ch.readOutbound(); + if (buffer == null) { + break; + } + read += buffer.readableBytes(); + buffer.release(); + } + + assertEquals(1024 * 16 * 3, read); + } + + @Test + public void testDiscardPendingWritesOnInactive() throws IOException { + + final AtomicBoolean closeWasCalled = new AtomicBoolean(false); + + ChunkedInput notifiableInput = new ChunkedInput() { + private boolean done; + private final ByteBuf buffer = Unpooled.copiedBuffer("Test", CharsetUtil.ISO_8859_1); + + @Override + public boolean isEndOfInput() throws Exception { + return done; + } + + @Override + public void close() throws Exception { + buffer.release(); + closeWasCalled.set(true); + } + + @Deprecated + @Override + public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { + return readChunk(ctx.alloc()); + } + + @Override + public ByteBuf readChunk(ByteBufAllocator allocator) throws Exception { + if (done) { + return null; + } + done = true; + return buffer.retainedDuplicate(); + } + + @Override + public long length() { + return -1; + } + + @Override + public long progress() { + return 1; + } + }; + + EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); + + // Write 3 messages and close channel before flushing + ChannelFuture r1 = ch.write(new ChunkedFile(TMP)); + ChannelFuture r2 = ch.write(new ChunkedNioFile(TMP)); + ch.write(notifiableInput); + + // Should be `false` as we do not expect any messages to be written + assertFalse(ch.finish()); + + assertFalse(r1.isSuccess()); + assertFalse(r2.isSuccess()); + assertTrue(closeWasCalled.get()); + } + + // See https://github.com/netty/netty/issues/8700. + @Test + public void testStopConsumingChunksWhenFailed() { + final ByteBuf buffer = Unpooled.copiedBuffer("Test", CharsetUtil.ISO_8859_1); + final AtomicInteger chunks = new AtomicInteger(0); + + ChunkedInput nonClosableInput = new ChunkedInput() { + @Override + public boolean isEndOfInput() throws Exception { + return chunks.get() >= 5; + } + + @Override + public void close() throws Exception { + // no-op + } + + @Deprecated + @Override + public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { + return readChunk(ctx.alloc()); + } + + @Override + public ByteBuf readChunk(ByteBufAllocator allocator) throws Exception { + chunks.incrementAndGet(); + return buffer.retainedDuplicate(); + } + + @Override + public long length() { + return -1; + } + + @Override + public long progress() { + return 1; + } + }; + + ChannelOutboundHandlerAdapter noOpWrites = new ChannelOutboundHandlerAdapter() { + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { + ReferenceCountUtil.release(msg); + promise.tryFailure(new RuntimeException()); + } + }; + + EmbeddedChannel ch = new EmbeddedChannel(noOpWrites, new ChunkedWriteHandler()); + ch.writeAndFlush(nonClosableInput).awaitUninterruptibly(); + // Should be `false` as we do not expect any messages to be written + assertFalse(ch.finish()); + buffer.release(); + + // We should expect only single chunked being read from the input. + // It's possible to get a race condition here between resolving a promise and + // allocating a new chunk, but should be fine when working with embedded channels. + assertEquals(1, chunks.get()); + } + private static void check(Object... inputs) { EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); @@ -255,4 +461,67 @@ private static void check(Object... inputs) { assertEquals(BYTES.length * inputs.length, read); } + + private static void checkFirstFailed(Object input) { + ChannelOutboundHandlerAdapter noOpWrites = new ChannelOutboundHandlerAdapter() { + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { + ReferenceCountUtil.release(msg); + promise.tryFailure(new RuntimeException()); + } + }; + + EmbeddedChannel ch = new EmbeddedChannel(noOpWrites, new ChunkedWriteHandler()); + ChannelFuture r = ch.writeAndFlush(input); + + // Should be `false` as we do not expect any messages to be written + assertFalse(ch.finish()); + assertTrue(r.cause() instanceof RuntimeException); + } + + private static void checkSkipFailed(Object input1, Object input2) { + ChannelOutboundHandlerAdapter failFirst = new ChannelOutboundHandlerAdapter() { + private boolean alreadyFailed; + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { + if (alreadyFailed) { + ctx.write(msg, promise); + } else { + this.alreadyFailed = true; + ReferenceCountUtil.release(msg); + promise.tryFailure(new RuntimeException()); + } + } + }; + + EmbeddedChannel ch = new EmbeddedChannel(failFirst, new ChunkedWriteHandler()); + ChannelFuture r1 = ch.write(input1); + ChannelFuture r2 = ch.writeAndFlush(input2).awaitUninterruptibly(); + assertTrue(ch.finish()); + + assertTrue(r1.cause() instanceof RuntimeException); + assertTrue(r2.isSuccess()); + + // note, that after we've "skipped" the first write, + // we expect to see the second message, chunk by chunk + int i = 0; + int read = 0; + for (;;) { + ByteBuf buffer = ch.readOutbound(); + if (buffer == null) { + break; + } + while (buffer.isReadable()) { + assertEquals(BYTES[i++], buffer.readByte()); + read++; + if (i == BYTES.length) { + i = 0; + } + } + buffer.release(); + } + + assertEquals(BYTES.length, read); + } } From 46fcc7bc97c279a35da69cd0207525c2fad3d860 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 17 Jan 2019 07:02:13 +0100 Subject: [PATCH 339/417] Allow to run builds with OpenJDK 13. (#8724) Motivation: There are the first EA bulds for OpenJDK 13. We should support to build with it and run builds on the CI. Modifications: - Add profile for JDK 13 - Add docker config to run with JDK 13. Result: Building and testing with OpenJDK 13 is possible. --- docker/docker-compose.centos-6.113.yaml | 22 ++++++++++++++++++++++ docker/docker-compose.centos-7.113.yaml | 22 ++++++++++++++++++++++ pom.xml | 22 ++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 docker/docker-compose.centos-6.113.yaml create mode 100644 docker/docker-compose.centos-7.113.yaml diff --git a/docker/docker-compose.centos-6.113.yaml b/docker/docker-compose.centos-6.113.yaml new file mode 100644 index 000000000000..c8260673b5d2 --- /dev/null +++ b/docker/docker-compose.centos-6.113.yaml @@ -0,0 +1,22 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-6-1.13 + build: + args: + centos_version : "6" + java_version : "openjdk@1.13.0-3" + + test: + image: netty:centos-6-1.13 + + test-leak: + image: netty:centos-6-1.13 + + test-boringssl-static: + image: netty:centos-6-1.13 + + shell: + image: netty:centos-6-1.13 diff --git a/docker/docker-compose.centos-7.113.yaml b/docker/docker-compose.centos-7.113.yaml new file mode 100644 index 000000000000..e49a045b1ddc --- /dev/null +++ b/docker/docker-compose.centos-7.113.yaml @@ -0,0 +1,22 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-7-1.13 + build: + args: + centos_version : "7" + java_version : "openjdk@1.13.0-3" + + test: + image: netty:centos-7-1.13 + + test-leak: + image: netty:centos-7-1.13 + + test-boringssl-static: + image: netty:centos-7-1.13 + + shell: + image: netty:centos-7-1.13 diff --git a/pom.xml b/pom.xml index 60bfa29c78b6..23980ff0cc0f 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,28 @@ + + + java13 + + 13 + + + + + true + + 3.0.0-M1 + + 2.0.5.Final + + 1.7 + 1.7 + + true + + + java12 From dd54c06e1e6ccd9c4d8c4e40b3c81837287aff0d Mon Sep 17 00:00:00 2001 From: Riyafa Abdul Hameed Date: Thu, 17 Jan 2019 11:47:12 +0530 Subject: [PATCH 340/417] Close connection for CorruptedFrameException (#8705) Motivation: The CorruptedFrameException from the finish() method of the Utf8Validator gets propagated to other handlers while the connection is still open. Modification: Override exceptionCaught method of the Utf8FrameValidator and close the connection if it is a CorruptedFrameException. Result: The CorruptedFrameException gets propagated to other handlers only after properly closing the connection. --- .../http/websocketx/Utf8FrameValidator.java | 27 ++++----- .../WebSocketUtf8FrameValidatorTest.java | 55 +++++++++++++++++++ 2 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketUtf8FrameValidatorTest.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8FrameValidator.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8FrameValidator.java index 55f07057ec08..5ce5ec369cb8 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8FrameValidator.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8FrameValidator.java @@ -47,7 +47,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception if ((frame instanceof TextWebSocketFrame) || (utf8Validator != null && utf8Validator.isChecking())) { // Check UTF-8 correctness for this payload - checkUTF8String(ctx, frame.content()); + checkUTF8String(frame.content()); // This does a second check to make sure UTF-8 // correctness for entire text message @@ -60,12 +60,12 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception if (fragmentedFramesCount == 0) { // First text or binary frame for a fragmented set if (frame instanceof TextWebSocketFrame) { - checkUTF8String(ctx, frame.content()); + checkUTF8String(frame.content()); } } else { // Subsequent frames - only check if init frame is text if (utf8Validator != null && utf8Validator.isChecking()) { - checkUTF8String(ctx, frame.content()); + checkUTF8String(frame.content()); } } @@ -77,17 +77,18 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception super.channelRead(ctx, msg); } - private void checkUTF8String(ChannelHandlerContext ctx, ByteBuf buffer) { - try { - if (utf8Validator == null) { - utf8Validator = new Utf8Validator(); - } - utf8Validator.check(buffer); - } catch (CorruptedFrameException ex) { - if (ctx.channel().isActive()) { - ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); - } + private void checkUTF8String(ByteBuf buffer) { + if (utf8Validator == null) { + utf8Validator = new Utf8Validator(); } + utf8Validator.check(buffer); } + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (cause instanceof CorruptedFrameException && ctx.channel().isOpen()) { + ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); + } + super.exceptionCaught(ctx, cause); + } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketUtf8FrameValidatorTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketUtf8FrameValidatorTest.java new file mode 100644 index 000000000000..c3bb0ed7dd3d --- /dev/null +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketUtf8FrameValidatorTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2019 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.http.websocketx; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.CorruptedFrameException; +import org.junit.Assert; +import org.junit.Test; + +public class WebSocketUtf8FrameValidatorTest { + + @Test + public void testCorruptedFrameExceptionInFinish() { + assertCorruptedFrameExceptionHandling(new byte[]{-50}); + } + + @Test + public void testCorruptedFrameExceptionInCheck() { + assertCorruptedFrameExceptionHandling(new byte[]{-8, -120, -128, -128, -128}); + } + + private void assertCorruptedFrameExceptionHandling(byte[] data) { + EmbeddedChannel channel = new EmbeddedChannel(new Utf8FrameValidator()); + try { + channel.writeInbound(new TextWebSocketFrame(Unpooled.copiedBuffer(data))); + Assert.fail(); + } catch (CorruptedFrameException e) { + // expected exception + } + Assert.assertTrue(channel.finish()); + ByteBuf buf = channel.readOutbound(); + Assert.assertNotNull(buf); + try { + Assert.assertFalse(buf.isReadable()); + } finally { + buf.release(); + } + Assert.assertNull(channel.readOutbound()); + } +} From c893939bd89243a1d6423ed86419bcf7748659ec Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 17 Jan 2019 09:14:27 +0100 Subject: [PATCH 341/417] Update to latest JDK8 and JDK11 releases (#8725) Motivation: We should always build with the latest JDK releases. Modifications: Update JDK8 and JDK11 versions to the latest. Result: Run CI jobs on the latest JDK release. --- docker/docker-compose.centos-6.111.yaml | 2 +- docker/docker-compose.centos-6.18.yaml | 2 +- docker/docker-compose.centos-7.111.yaml | 2 +- docker/docker-compose.centos-7.18.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml index 12b19d9189ac..73618c749ea2 100644 --- a/docker/docker-compose.centos-6.111.yaml +++ b/docker/docker-compose.centos-6.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "openjdk@1.11.0-1" + java_version : "openjdk@1.11.0-2" test: image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-6.18.yaml b/docker/docker-compose.centos-6.18.yaml index 43817e5c0e24..ecde4daf0bf9 100644 --- a/docker/docker-compose.centos-6.18.yaml +++ b/docker/docker-compose.centos-6.18.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "1.8.192" + java_version : "1.8.202" test: image: netty:centos-6-1.8 diff --git a/docker/docker-compose.centos-7.111.yaml b/docker/docker-compose.centos-7.111.yaml index f80a7355e701..75d635d25b3f 100644 --- a/docker/docker-compose.centos-7.111.yaml +++ b/docker/docker-compose.centos-7.111.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "openjdk@1.11.0-1" + java_version : "openjdk@1.11.0-2" test: image: netty:centos-7-1.11 diff --git a/docker/docker-compose.centos-7.18.yaml b/docker/docker-compose.centos-7.18.yaml index 20008caa04f9..327920119743 100644 --- a/docker/docker-compose.centos-7.18.yaml +++ b/docker/docker-compose.centos-7.18.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "1.8.192" + java_version : "1.8.202" test: image: netty:centos-7-1.8 From 8ebaa1b9721c9a6e2d4c85ce833143878a0895c1 Mon Sep 17 00:00:00 2001 From: kezhenxu94 Date: Sat, 19 Jan 2019 02:44:47 +0800 Subject: [PATCH 342/417] enhancement: extract duplicate code (#8732) Motivation: Clean up code to increase readability. Modification: Extract duplicate code and remove unnecessary throws Result: Share more code. --- .../netty/channel/DefaultChannelPipeline.java | 111 ++++++++---------- 1 file changed, 48 insertions(+), 63 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java index 2155a7cbb379..2b307cc911f4 100644 --- a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java @@ -53,7 +53,7 @@ public class DefaultChannelPipeline implements ChannelPipeline { private static final FastThreadLocal, String>> nameCaches = new FastThreadLocal, String>>() { @Override - protected Map, String> initialValue() throws Exception { + protected Map, String> initialValue() { return new WeakHashMap, String>(); } }; @@ -163,7 +163,7 @@ public final ChannelPipeline addFirst(EventExecutorGroup group, String name, Cha addFirst0(newCtx); - // If the registered is false it means that the channel was not registered on an eventloop yet. + // If the registered is false it means that the channel was not registered on an eventLoop yet. // In this case we add the context to the pipeline and add a task that will call // ChannelHandler.handlerAdded(...) once the channel is registered. if (!registered) { @@ -174,13 +174,7 @@ public final ChannelPipeline addFirst(EventExecutorGroup group, String name, Cha EventExecutor executor = newCtx.executor(); if (!executor.inEventLoop()) { - newCtx.setAddPending(); - executor.execute(new Runnable() { - @Override - public void run() { - callHandlerAdded0(newCtx); - } - }); + callHandlerAddedInEventLoop(newCtx, executor); return this; } } @@ -211,7 +205,7 @@ public final ChannelPipeline addLast(EventExecutorGroup group, String name, Chan addLast0(newCtx); - // If the registered is false it means that the channel was not registered on an eventloop yet. + // If the registered is false it means that the channel was not registered on an eventLoop yet. // In this case we add the context to the pipeline and add a task that will call // ChannelHandler.handlerAdded(...) once the channel is registered. if (!registered) { @@ -222,13 +216,7 @@ public final ChannelPipeline addLast(EventExecutorGroup group, String name, Chan EventExecutor executor = newCtx.executor(); if (!executor.inEventLoop()) { - newCtx.setAddPending(); - executor.execute(new Runnable() { - @Override - public void run() { - callHandlerAdded0(newCtx); - } - }); + callHandlerAddedInEventLoop(newCtx, executor); return this; } } @@ -263,7 +251,7 @@ public final ChannelPipeline addBefore( addBefore0(ctx, newCtx); - // If the registered is false it means that the channel was not registered on an eventloop yet. + // If the registered is false it means that the channel was not registered on an eventLoop yet. // In this case we add the context to the pipeline and add a task that will call // ChannelHandler.handlerAdded(...) once the channel is registered. if (!registered) { @@ -274,13 +262,7 @@ public final ChannelPipeline addBefore( EventExecutor executor = newCtx.executor(); if (!executor.inEventLoop()) { - newCtx.setAddPending(); - executor.execute(new Runnable() { - @Override - public void run() { - callHandlerAdded0(newCtx); - } - }); + callHandlerAddedInEventLoop(newCtx, executor); return this; } } @@ -323,7 +305,7 @@ public final ChannelPipeline addAfter( addAfter0(ctx, newCtx); - // If the registered is false it means that the channel was not registered on an eventloop yet. + // If the registered is false it means that the channel was not registered on an eventLoop yet. // In this case we remove the context from the pipeline and add a task that will call // ChannelHandler.handlerRemoved(...) once the channel is registered. if (!registered) { @@ -333,13 +315,7 @@ public final ChannelPipeline addAfter( } EventExecutor executor = newCtx.executor(); if (!executor.inEventLoop()) { - newCtx.setAddPending(); - executor.execute(new Runnable() { - @Override - public void run() { - callHandlerAdded0(newCtx); - } - }); + callHandlerAddedInEventLoop(newCtx, executor); return this; } } @@ -1168,6 +1144,16 @@ private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean } } + private void callHandlerAddedInEventLoop(final AbstractChannelHandlerContext newCtx, EventExecutor executor) { + newCtx.setAddPending(); + executor.execute(new Runnable() { + @Override + public void run() { + callHandlerAdded0(newCtx); + } + }); + } + /** * Called once a {@link Throwable} hit the end of the {@link ChannelPipeline} without been handled by the user * in {@link ChannelHandler#exceptionCaught(ChannelHandlerContext, Throwable)}. @@ -1267,49 +1253,49 @@ public ChannelHandler handler() { } @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { } + public void channelRegistered(ChannelHandlerContext ctx) { } @Override - public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { } + public void channelUnregistered(ChannelHandlerContext ctx) { } @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { + public void channelActive(ChannelHandlerContext ctx) { onUnhandledInboundChannelActive(); } @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { + public void channelInactive(ChannelHandlerContext ctx) { onUnhandledInboundChannelInactive(); } @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { + public void channelWritabilityChanged(ChannelHandlerContext ctx) { onUnhandledChannelWritabilityChanged(); } @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { } + public void handlerAdded(ChannelHandlerContext ctx) { } @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { } + public void handlerRemoved(ChannelHandlerContext ctx) { } @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { onUnhandledInboundUserEventTriggered(evt); } @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { onUnhandledInboundException(cause); } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + public void channelRead(ChannelHandlerContext ctx, Object msg) { onUnhandledInboundMessage(msg); } @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + public void channelReadComplete(ChannelHandlerContext ctx) { onUnhandledInboundChannelReadComplete(); } } @@ -1331,19 +1317,18 @@ public ChannelHandler handler() { } @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + public void handlerAdded(ChannelHandlerContext ctx) { // NOOP } @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + public void handlerRemoved(ChannelHandlerContext ctx) { // NOOP } @Override public void bind( - ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) - throws Exception { + ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) { unsafe.bind(localAddress, promise); } @@ -1351,22 +1336,22 @@ public void bind( public void connect( ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, - ChannelPromise promise) throws Exception { + ChannelPromise promise) { unsafe.connect(remoteAddress, localAddress, promise); } @Override - public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) { unsafe.disconnect(promise); } @Override - public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + public void close(ChannelHandlerContext ctx, ChannelPromise promise) { unsafe.close(promise); } @Override - public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) { unsafe.deregister(promise); } @@ -1376,28 +1361,28 @@ public void read(ChannelHandlerContext ctx) { } @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { unsafe.write(msg, promise); } @Override - public void flush(ChannelHandlerContext ctx) throws Exception { + public void flush(ChannelHandlerContext ctx) { unsafe.flush(); } @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { ctx.fireExceptionCaught(cause); } @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + public void channelRegistered(ChannelHandlerContext ctx) { invokeHandlerAddedIfNeeded(); ctx.fireChannelRegistered(); } @Override - public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { + public void channelUnregistered(ChannelHandlerContext ctx) { ctx.fireChannelUnregistered(); // Remove all handlers sequentially if channel is closed and unregistered. @@ -1407,24 +1392,24 @@ public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { } @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { + public void channelActive(ChannelHandlerContext ctx) { ctx.fireChannelActive(); readIfIsAutoRead(); } @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { + public void channelInactive(ChannelHandlerContext ctx) { ctx.fireChannelInactive(); } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + public void channelRead(ChannelHandlerContext ctx, Object msg) { ctx.fireChannelRead(msg); } @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + public void channelReadComplete(ChannelHandlerContext ctx) { ctx.fireChannelReadComplete(); readIfIsAutoRead(); @@ -1437,12 +1422,12 @@ private void readIfIsAutoRead() { } @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { ctx.fireUserEventTriggered(evt); } @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { + public void channelWritabilityChanged(ChannelHandlerContext ctx) { ctx.fireChannelWritabilityChanged(); } } From e4b9d5f9a17a34e2ef8ea6fed901693f61ff5f92 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 18 Jan 2019 20:13:49 +0100 Subject: [PATCH 343/417] Skip osgi testsuite on JDK11. (#8733) Motivation: Since the updating to OpenJDK 11.0.2 the OSGI testsuite fails. We should dissable it until there is a version of the used plugins that works with this OpenJDK version. Modifications: Skip osgi testsuite when using JDK11. Result: Build pass again with JDK11. --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 23980ff0cc0f..cc130091c6c2 100644 --- a/pom.xml +++ b/pom.xml @@ -126,6 +126,8 @@ 3.0.0-M1 2.0.5.Final + + true From df5eb060f729a18582d659f868f764edc9a7277e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 18 Jan 2019 21:06:44 +0100 Subject: [PATCH 344/417] Only handle NXDOMAIN as failure when nameserver is authoritive or no other nameservers are left. (#8731) Motivation: When using multiple nameservers and a nameserver respond with NXDOMAIN we should only fail the query if the nameserver in question is authoritive or no nameservers are left to try. Modifications: - Try next nameserver if NXDOMAIN was returned but the nameserver is not authoritive - Adjust testcase to respect correct behaviour. Result: Fixes https://github.com/netty/netty/issues/8261 --- .../netty/resolver/dns/DnsResolveContext.java | 23 +++++++++++++++++++ .../resolver/dns/DnsNameResolverTest.java | 10 ++------ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java index 847bfb5b9f3c..f625be975cea 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java @@ -497,6 +497,29 @@ private void onResponse(final DnsServerAddressStream nameServerAddrStream, final queryLifecycleObserver.queryNoAnswer(code), true, promise, null); } else { queryLifecycleObserver.queryFailed(NXDOMAIN_QUERY_FAILED_EXCEPTION); + + // Try with the next server if is not authoritative for the domain. + // + // From https://tools.ietf.org/html/rfc1035 : + // + // RCODE Response code - this 4 bit field is set as part of + // responses. The values have the following + // interpretation: + // + // .... + // .... + // + // 3 Name Error - Meaningful only for + // responses from an authoritative name + // server, this code signifies that the + // domain name referenced in the query does + // not exist. + // .... + // .... + if (!res.isAuthoritativeAnswer()) { + query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, + newDnsQueryLifecycleObserver(question), true, promise, null); + } } } finally { ReferenceCountUtil.safeRelease(envelope); diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java index 1326a7975e8d..d03ee8707757 100644 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java @@ -1143,7 +1143,7 @@ public void aAndAAAAQueryShouldTryFirstDnsServerBeforeSecond() throws IOExceptio new TestRecursiveCacheDnsQueryLifecycleObserverFactory(); DnsNameResolverBuilder builder = new DnsNameResolverBuilder(group.next()) - .resolvedAddressTypes(ResolvedAddressTypes.IPV6_PREFERRED) + .resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY) .dnsQueryLifecycleObserverFactory(lifecycleObserverFactory) .channelType(NioDatagramChannel.class) .optResourceEnabled(false) @@ -1156,18 +1156,12 @@ public void aAndAAAAQueryShouldTryFirstDnsServerBeforeSecond() throws IOExceptio TestDnsQueryLifecycleObserver observer = lifecycleObserverFactory.observers.poll(); assertNotNull(observer); - assertEquals(2, lifecycleObserverFactory.observers.size()); + assertEquals(1, lifecycleObserverFactory.observers.size()); assertEquals(2, observer.events.size()); QueryWrittenEvent writtenEvent = (QueryWrittenEvent) observer.events.poll(); assertEquals(dnsServer1.localAddress(), writtenEvent.dnsServerAddress); QueryFailedEvent failedEvent = (QueryFailedEvent) observer.events.poll(); - observer = lifecycleObserverFactory.observers.poll(); - assertEquals(2, observer.events.size()); - writtenEvent = (QueryWrittenEvent) observer.events.poll(); - assertEquals(dnsServer1.localAddress(), writtenEvent.dnsServerAddress); - failedEvent = (QueryFailedEvent) observer.events.poll(); - observer = lifecycleObserverFactory.observers.poll(); assertEquals(2, observer.events.size()); writtenEvent = (QueryWrittenEvent) observer.events.poll(); From dae5d9d3f9d4652f9b5e99edd4709a3e57a46277 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 19 Jan 2019 14:01:31 +0100 Subject: [PATCH 345/417] =?UTF-8?q?Ensure=20FlowControlled=20data=20frames?= =?UTF-8?q?=20will=20be=20correctly=20removed=20from=20the=20=E2=80=A6=20(?= =?UTF-8?q?#8726)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: When a write error happens during writing of flowcontrolled data frames we miss to correctly detect this in the write loop which may result in an infinite loop as we will never detect that the frame should be removed from the queue. Modifications: - When we fail a flowcontrolled data frame we ensure that the next frame.write(...) call will signal back that the whole frame was handled and so can be removed. - Add unit test. Result: Fixes https://github.com/netty/netty/issues/8707. --- .../http2/DefaultHttp2ConnectionEncoder.java | 23 ++++++-- .../DefaultHttp2ConnectionEncoderTest.java | 38 +++++++++++++ .../http2/Http2ConnectionRoundtripTest.java | 57 ++++++++++++++++++- 3 files changed, 112 insertions(+), 6 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java index c21081094b33..ce90e24342e4 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java @@ -397,6 +397,9 @@ public void error(ChannelHandlerContext ctx, Throwable cause) { queue.releaseAndFailAll(cause); // Don't update dataSize because we need to ensure the size() method returns a consistent size even after // error so we don't invalidate flow control when returning bytes to flow control. + // + // That said we will set dataSize and padding to 0 in the write(...) method if we cleared the queue + // because of an error. lifecycleManager.onError(ctx, true, cause); } @@ -405,11 +408,21 @@ public void write(ChannelHandlerContext ctx, int allowedBytes) { int queuedData = queue.readableBytes(); if (!endOfStream) { if (queuedData == 0) { - // 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 corresponding - // to 0 bytes and writing it to the channel (to preserve notification order). - ChannelPromise writePromise = ctx.newPromise().addListener(this); - ctx.write(queue.remove(0, writePromise), writePromise); + if (queue.isEmpty()) { + // When the queue is empty it means we did clear it because of an error(...) call + // (as otherwise we will have at least 1 entry in there), which will happen either when called + // explicit or when the write itself fails. In this case just set dataSize and padding to 0 + // which will signal back that the whole frame was consumed. + // + // See https://github.com/netty/netty/issues/8707. + padding = dataSize = 0; + } else { + // 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 + // corresponding to 0 bytes and writing it to the channel (to preserve notification order). + ChannelPromise writePromise = ctx.newPromise().addListener(this); + ctx.write(queue.remove(0, writePromise), writePromise); + } return; } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java index 6be47462c3c0..9ca7b1fee1b0 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java @@ -55,6 +55,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -126,6 +127,13 @@ public void setup() throws Exception { when(channel.unsafe()).thenReturn(unsafe); ChannelConfig config = new DefaultChannelConfig(channel); when(channel.config()).thenReturn(config); + doAnswer(new Answer() { + @Override + public ChannelFuture answer(InvocationOnMock in) { + return newPromise().setFailure((Throwable) in.getArgument(0)); + } + }).when(channel).newFailedFuture(any(Throwable.class)); + when(writer.configuration()).thenReturn(writerConfig); when(writerConfig.frameSizePolicy()).thenReturn(frameSizePolicy); when(frameSizePolicy.maxFrameSize()).thenReturn(64); @@ -206,6 +214,36 @@ public ChannelFuture answer(InvocationOnMock in) throws Throwable { encoder.lifecycleManager(lifecycleManager); } + @Test + public void dataWithEndOfStreamWriteShouldSignalThatFrameWasConsumedOnError() throws Exception { + dataWriteShouldSignalThatFrameWasConsumedOnError0(true); + } + + @Test + public void dataWriteShouldSignalThatFrameWasConsumedOnError() throws Exception { + dataWriteShouldSignalThatFrameWasConsumedOnError0(false); + } + + private void dataWriteShouldSignalThatFrameWasConsumedOnError0(boolean endOfStream) throws Exception { + createStream(STREAM_ID, false); + final ByteBuf data = dummyData(); + ChannelPromise p = newPromise(); + encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream, p); + + FlowControlled controlled = payloadCaptor.getValue(); + assertEquals(8, controlled.size()); + payloadCaptor.getValue().write(ctx, 4); + assertEquals(4, controlled.size()); + + Throwable error = new IllegalStateException(); + payloadCaptor.getValue().error(ctx, error); + payloadCaptor.getValue().write(ctx, 8); + assertEquals(0, controlled.size()); + assertEquals("abcd", writtenData.get(0)); + assertEquals(0, data.refCnt()); + assertSame(error, p.cause()); + } + @Test public void dataWriteShouldSucceed() throws Exception { createStream(STREAM_ID, false); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java index f4a4b6c94bcd..32febac05f41 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java @@ -27,6 +27,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.channel.DefaultEventLoopGroup; @@ -37,6 +38,7 @@ import io.netty.handler.codec.http2.Http2TestUtil.Http2Runnable; import io.netty.util.AsciiString; import io.netty.util.IllegalReferenceCountException; +import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.Future; import org.junit.After; import org.junit.Before; @@ -682,7 +684,7 @@ public void writeOfEmptyReleasedBufferMultipleBuffersTrailersQueuedInFlowControl writeOfEmptyReleasedBufferQueuedInFlowControllerShouldFail(WriteEmptyBufferMode.SECOND_WITH_TRAILERS); } - public void writeOfEmptyReleasedBufferQueuedInFlowControllerShouldFail(final WriteEmptyBufferMode mode) + private void writeOfEmptyReleasedBufferQueuedInFlowControllerShouldFail(final WriteEmptyBufferMode mode) throws Exception { bootstrapEnv(1, 1, 2, 1); @@ -728,6 +730,59 @@ public void run() throws Http2Exception { } } + @Test + public void writeFailureFlowControllerRemoveFrame() + throws Exception { + bootstrapEnv(1, 1, 2, 1); + + final ChannelPromise dataPromise = newPromise(); + final ChannelPromise assertPromise = newPromise(); + + runInChannel(clientChannel, new Http2Runnable() { + @Override + public void run() throws Http2Exception { + http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false, + newPromise()); + clientChannel.pipeline().addFirst(new ChannelOutboundHandlerAdapter() { + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + ReferenceCountUtil.release(msg); + + // Ensure we update the window size so we will try to write the rest of the frame while + // processing the flush. + http2Client.encoder().flowController().initialWindowSize(8); + promise.setFailure(new IllegalStateException()); + } + }); + + http2Client.encoder().flowController().initialWindowSize(4); + http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false, dataPromise); + assertTrue(http2Client.encoder().flowController() + .hasFlowControlled(http2Client.connection().stream(3))); + + http2Client.flush(ctx()); + + try { + // The Frame should have been removed after the write failed. + assertFalse(http2Client.encoder().flowController() + .hasFlowControlled(http2Client.connection().stream(3))); + assertPromise.setSuccess(); + } catch (Throwable error) { + assertPromise.setFailure(error); + } + } + }); + + try { + dataPromise.get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause(), is(instanceOf(IllegalStateException.class))); + } + + assertPromise.sync(); + } + @Test public void nonHttp2ExceptionInPipelineShouldNotCloseConnection() throws Exception { bootstrapEnv(1, 1, 2, 1); From bce0784e5eb72c395eff3fac59152b65bddc2c7e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 19 Jan 2019 17:17:03 +0100 Subject: [PATCH 346/417] Fix racy ChannelOutboundBuffer.testWriteTaskRejected test. (#8735) Motivation: testWriteTaskRejected was racy as we did not ensure we dispatched all events to the executor before shutting it down. Modifications: Add a latch to ensure we dispatched everything. Result: Fix racy test that failed sometimes before. --- .../channel/ChannelOutboundBufferTest.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/transport/src/test/java/io/netty/channel/ChannelOutboundBufferTest.java b/transport/src/test/java/io/netty/channel/ChannelOutboundBufferTest.java index 955de75395da..c58569c43a31 100644 --- a/transport/src/test/java/io/netty/channel/ChannelOutboundBufferTest.java +++ b/transport/src/test/java/io/netty/channel/ChannelOutboundBufferTest.java @@ -383,17 +383,23 @@ protected Queue newTaskQueue(int maxPendingTasks) { } }; final CountDownLatch handlerAddedLatch = new CountDownLatch(1); + final CountDownLatch handlerRemovedLatch = new CountDownLatch(1); EmbeddedChannel ch = new EmbeddedChannel(); - ch.pipeline().addLast(executor, new ChannelOutboundHandlerAdapter() { + ch.pipeline().addLast(executor, "handler", new ChannelOutboundHandlerAdapter() { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { promise.setFailure(new AssertionError("Should not be called")); } @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + public void handlerAdded(ChannelHandlerContext ctx) { handlerAddedLatch.countDown(); } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) { + handlerRemovedLatch.countDown(); + } }); // Lets wait until we are sure the handler was added. @@ -436,7 +442,19 @@ public void run() { assertEquals(0, ch.unsafe().outboundBuffer().totalPendingWriteBytes()); executeLatch.countDown(); + while (executor.pendingTasks() != 0) { + // Wait until there is no more pending task left. + Thread.sleep(10); + } + + ch.pipeline().remove("handler"); + + // Ensure we do not try to shutdown the executor before we handled everything for the Channel. Otherwise + // the Executor may reject when the Channel tries to add a task to it. + handlerRemovedLatch.await(); + safeClose(ch); + executor.shutdownGracefully(); } From a2cd246f00a5a1332dc9e8c6f4375e06bed300b0 Mon Sep 17 00:00:00 2001 From: kezhenxu94 Date: Sun, 20 Jan 2019 00:40:55 +0800 Subject: [PATCH 347/417] cleanup: fix indent (#8734) Motivation: Clean up to make the code style unified. Modification: Fix indent Result: Indents are unified --- transport/src/main/java/io/netty/channel/ChannelPipeline.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/src/main/java/io/netty/channel/ChannelPipeline.java b/transport/src/main/java/io/netty/channel/ChannelPipeline.java index 9652637278e0..c415d8536d5e 100644 --- a/transport/src/main/java/io/netty/channel/ChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/ChannelPipeline.java @@ -597,7 +597,7 @@ T replace(Class oldHandlerType, String newName, @Override ChannelPipeline fireChannelRegistered(); - @Override + @Override ChannelPipeline fireChannelUnregistered(); @Override From 1e4481e551696a57c79d3f1acd588171e12e082b Mon Sep 17 00:00:00 2001 From: yulianoifa-mobius Date: Mon, 21 Jan 2019 08:42:05 +0200 Subject: [PATCH 348/417] Allowed IP_FREEBIND option for UDP epoll (#8728) Motivation: While using Load Balancers or HA support is needed there are cases when UDP channel need to bind to IP Address which is not available on network interfaces locally. Modification: Modified EpollDatagramChannelConfig to allow IP_FREEBIND option Result: Fixes ##8727. --- .../epoll/EpollDatagramChannelConfig.java | 32 ++++++++++++++++++- .../epoll/EpollDatagramChannelConfigTest.java | 32 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramChannelConfigTest.java diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java index f3de6ac5947b..778b555fa195 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java @@ -47,7 +47,7 @@ public Map, Object> getOptions() { ChannelOption.SO_REUSEADDR, ChannelOption.IP_MULTICAST_LOOP_DISABLED, ChannelOption.IP_MULTICAST_ADDR, ChannelOption.IP_MULTICAST_IF, ChannelOption.IP_MULTICAST_TTL, ChannelOption.IP_TOS, ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION, - EpollChannelOption.SO_REUSEPORT, EpollChannelOption.IP_TRANSPARENT, + EpollChannelOption.SO_REUSEPORT, EpollChannelOption.IP_FREEBIND, EpollChannelOption.IP_TRANSPARENT, EpollChannelOption.IP_RECVORIGDSTADDR); } @@ -90,6 +90,9 @@ public T getOption(ChannelOption option) { if (option == EpollChannelOption.IP_TRANSPARENT) { return (T) Boolean.valueOf(isIpTransparent()); } + if (option == EpollChannelOption.IP_FREEBIND) { + return (T) Boolean.valueOf(isFreeBind()); + } if (option == EpollChannelOption.IP_RECVORIGDSTADDR) { return (T) Boolean.valueOf(isIpRecvOrigDestAddr()); } @@ -123,6 +126,8 @@ public boolean setOption(ChannelOption option, T value) { setActiveOnOpen((Boolean) value); } else if (option == EpollChannelOption.SO_REUSEPORT) { setReusePort((Boolean) value); + } else if (option == EpollChannelOption.IP_FREEBIND) { + setFreeBind((Boolean) value); } else if (option == EpollChannelOption.IP_TRANSPARENT) { setIpTransparent((Boolean) value); } else if (option == EpollChannelOption.IP_RECVORIGDSTADDR) { @@ -407,6 +412,31 @@ public EpollDatagramChannelConfig setIpTransparent(boolean ipTransparent) { } } + /** + * Returns {@code true} if IP_FREEBIND is enabled, + * {@code false} otherwise. + */ + public boolean isFreeBind() { + try { + return ((EpollDatagramChannel) channel).socket.isIpFreeBind(); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + /** + * If {@code true} is used IP_FREEBIND is enabled, + * {@code false} for disable it. Default is disabled. + */ + public EpollDatagramChannelConfig setFreeBind(boolean freeBind) { + try { + ((EpollDatagramChannel) channel).socket.setIpFreeBind(freeBind); + return this; + } catch (IOException e) { + throw new ChannelException(e); + } + } + /** * Returns {@code true} if IP_RECVORIGDSTADDR is * enabled, {@code false} otherwise. diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramChannelConfigTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramChannelConfigTest.java new file mode 100644 index 000000000000..39cf7acd064b --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramChannelConfigTest.java @@ -0,0 +1,32 @@ +/* + * Copyright 2019 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: + * + * http://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.channel.epoll; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class EpollDatagramChannelConfigTest { + + @Test + public void testIpFreeBind() throws Exception { + Epoll.ensureAvailability(); + EpollDatagramChannel channel = new EpollDatagramChannel(); + assertTrue(channel.config().setOption(EpollChannelOption.IP_FREEBIND, true)); + assertTrue(channel.config().getOption(EpollChannelOption.IP_FREEBIND)); + channel.fd().close(); + } +} From 83b286f5d9587dcb7ff944166ab75a2284c717c7 Mon Sep 17 00:00:00 2001 From: Bartek Kowalczyk Date: Mon, 21 Jan 2019 07:45:03 +0100 Subject: [PATCH 349/417] Set result for decoded request and add test for #8721 (#8721) Motivation: I want to fix bug in vert.x project (eclipse-vertx/vert.x#2562) caused by ComposedLastHttpContent result being null. I don't know if it is intentional that this last decoded chuck in the issue returns null, but if not - I am providing fix for that. Modification: * Added new constructor in ComposedLastHttpContent allowing to pass DecoderResult * set DecoderResult.SUCCESS for created ComposedLastHttpContent in HttpContentEncoder * set DecoderResult.SUCCESS for created ComposedLastHttpContent in HttpContentDecoder Result: Fixes eclipse-vertx/vert.x#2562 --- .../codec/http/ComposedLastHttpContent.java | 5 +++ .../codec/http/HttpContentDecoder.java | 3 +- .../codec/http/HttpContentEncoder.java | 3 +- .../codec/http/HttpContentCompressorTest.java | 4 ++ .../codec/http/HttpContentDecoderTest.java | 42 +++++++++++++++++++ .../codec/http/HttpContentEncoderTest.java | 3 ++ 6 files changed, 58 insertions(+), 2 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/ComposedLastHttpContent.java b/codec-http/src/main/java/io/netty/handler/codec/http/ComposedLastHttpContent.java index b8b7dc967fac..1ef39e56fff7 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/ComposedLastHttpContent.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/ComposedLastHttpContent.java @@ -28,6 +28,11 @@ final class ComposedLastHttpContent implements LastHttpContent { this.trailingHeaders = trailingHeaders; } + ComposedLastHttpContent(HttpHeaders trailingHeaders, DecoderResult result) { + this(trailingHeaders); + this.result = result; + } + @Override public HttpHeaders trailingHeaders() { return trailingHeaders; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java index e85adaaa373c..3eb7ad3e338e 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java @@ -19,6 +19,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.CodecException; +import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.util.ReferenceCountUtil; @@ -164,7 +165,7 @@ private void decodeContent(HttpContent c, List out) { if (headers.isEmpty()) { out.add(LastHttpContent.EMPTY_LAST_CONTENT); } else { - out.add(new ComposedLastHttpContent(headers)); + out.add(new ComposedLastHttpContent(headers, DecoderResult.SUCCESS)); } } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java index 0078edc42134..c486a67ed8a9 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBufHolder; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.MessageToMessageCodec; import io.netty.util.ReferenceCountUtil; @@ -264,7 +265,7 @@ private boolean encodeContent(HttpContent c, List out) { if (headers.isEmpty()) { out.add(LastHttpContent.EMPTY_LAST_CONTENT); } else { - out.add(new ComposedLastHttpContent(headers)); + out.add(new ComposedLastHttpContent(headers, DecoderResult.SUCCESS)); } return true; } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java index 6ab2ab88c629..508eb5ae54c6 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java @@ -18,6 +18,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.EncoderException; import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.util.CharsetUtil; @@ -188,6 +189,7 @@ public void testChunkedContentWithTrailingHeader() throws Exception { assertThat(chunk.content().isReadable(), is(false)); assertThat(chunk, is(instanceOf(LastHttpContent.class))); assertEquals("Netty", ((LastHttpContent) chunk).trailingHeaders().get(of("X-Test"))); + assertEquals(DecoderResult.SUCCESS, chunk.decoderResult()); chunk.release(); assertThat(ch.readOutbound(), is(nullValue())); @@ -331,6 +333,7 @@ public void testEmptyFullContentWithTrailer() throws Exception { assertThat(res.content().readableBytes(), is(0)); assertThat(res.content().toString(CharsetUtil.US_ASCII), is("")); assertEquals("Netty", res.trailingHeaders().get(of("X-Test"))); + assertEquals(DecoderResult.SUCCESS, res.decoderResult()); assertThat(ch.readOutbound(), is(nullValue())); } @@ -370,6 +373,7 @@ public void test100Continue() throws Exception { assertThat(res.content().readableBytes(), is(0)); assertThat(res.content().toString(CharsetUtil.US_ASCII), is("")); assertEquals("Netty", res.trailingHeaders().get(of("X-Test"))); + assertEquals(DecoderResult.SUCCESS, res.decoderResult()); assertThat(ch.readOutbound(), is(nullValue())); } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentDecoderTest.java index 7a27a4c08acf..ff0628527a3c 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentDecoderTest.java @@ -89,6 +89,48 @@ public void testRequestDecompression() { assertFalse(channel.finish()); // assert that no messages are left in channel } + @Test + public void testChunkedRequestDecompression() { + HttpResponseDecoder decoder = new HttpResponseDecoder(); + HttpContentDecoder decompressor = new HttpContentDecompressor(); + + EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor, null); + + String headers = "HTTP/1.1 200 OK\r\n" + + "Transfer-Encoding: chunked\r\n" + + "Trailer: My-Trailer\r\n" + + "Content-Encoding: gzip\r\n\r\n"; + + channel.writeInbound(Unpooled.copiedBuffer(headers.getBytes(CharsetUtil.US_ASCII))); + + String chunkLength = Integer.toHexString(GZ_HELLO_WORLD.length); + assertTrue(channel.writeInbound(Unpooled.copiedBuffer(chunkLength + "\r\n", CharsetUtil.US_ASCII))); + assertTrue(channel.writeInbound(Unpooled.copiedBuffer(GZ_HELLO_WORLD))); + assertTrue(channel.writeInbound(Unpooled.copiedBuffer("\r\n".getBytes(CharsetUtil.US_ASCII)))); + assertTrue(channel.writeInbound(Unpooled.copiedBuffer("0\r\n", CharsetUtil.US_ASCII))); + assertTrue(channel.writeInbound(Unpooled.copiedBuffer("My-Trailer: 42\r\n\r\n\r\n", CharsetUtil.US_ASCII))); + + Object ob1 = channel.readInbound(); + assertThat(ob1, is(instanceOf(DefaultHttpResponse.class))); + + Object ob2 = channel.readInbound(); + assertThat(ob1, is(instanceOf(DefaultHttpResponse.class))); + HttpContent content = (HttpContent) ob2; + assertEquals(HELLO_WORLD, content.content().toString(CharsetUtil.US_ASCII)); + content.release(); + + Object ob3 = channel.readInbound(); + assertThat(ob1, is(instanceOf(DefaultHttpResponse.class))); + LastHttpContent lastContent = (LastHttpContent) ob3; + assertNotNull(lastContent.decoderResult()); + assertTrue(lastContent.decoderResult().isSuccess()); + assertFalse(lastContent.trailingHeaders().isEmpty()); + assertEquals("42", lastContent.trailingHeaders().get("My-Trailer")); + assertHasInboundMessages(channel, false); + assertHasOutboundMessages(channel, false); + assertFalse(channel.finish()); + } + @Test public void testResponseDecompression() { // baseline test: response decoder, content decompressor && request aggregator work as expected diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java index 9242cf872833..6301ee8c0b56 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java @@ -22,6 +22,7 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.CodecException; +import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.EncoderException; import io.netty.handler.codec.MessageToByteEncoder; import io.netty.util.CharsetUtil; @@ -156,6 +157,7 @@ public void testChunkedContentWithTrailingHeader() throws Exception { assertThat(chunk.content().isReadable(), is(false)); assertThat(chunk, is(instanceOf(LastHttpContent.class))); assertEquals("Netty", ((LastHttpContent) chunk).trailingHeaders().get(of("X-Test"))); + assertEquals(DecoderResult.SUCCESS, res.decoderResult()); chunk.release(); assertThat(ch.readOutbound(), is(nullValue())); @@ -285,6 +287,7 @@ public void testEmptyFullContentWithTrailer() throws Exception { assertThat(res.content().readableBytes(), is(0)); assertThat(res.content().toString(CharsetUtil.US_ASCII), is("")); assertEquals("Netty", res.trailingHeaders().get(of("X-Test"))); + assertEquals(DecoderResult.SUCCESS, res.decoderResult()); assertThat(ch.readOutbound(), is(nullValue())); } From fabc6ee1bc9a31b25e9b1adba8c9521f6ae28f43 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 21 Jan 2019 09:01:04 +0100 Subject: [PATCH 350/417] Fix flaky ChannelInitializerTest.testChannelInitializerEventExecutor() (#8738) Motivation: testChannelInitializerEventExecutor() did sometimes fail as we sometimes miss to count down the latch. This can happen when we remove the handler from the pipeline before channelUnregistered(...) was called for it. Modifications: Countdown the latch in handlerRemoved(...). Result: Fix flaky test. --- .../src/test/java/io/netty/channel/ChannelInitializerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java b/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java index bebf2d5e58d3..bd9415b17b1e 100644 --- a/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java +++ b/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java @@ -335,7 +335,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { } @Override - public void channelUnregistered(ChannelHandlerContext ctx) { + public void handlerRemoved(ChannelHandlerContext ctx) { latch.countDown(); } }); @@ -369,6 +369,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { client.closeFuture().sync(); server.closeFuture().sync(); + // Wait until the handler is removed from the pipeline and so no more events are handled by it. latch.await(); assertEquals(1, invokeCount.get()); From 9c192254c464e3a73ae1930eba9dc4c0c2432d61 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 21 Jan 2019 11:54:39 +0100 Subject: [PATCH 351/417] Remove duplicated declaration of dependency --- codec-http2/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 92e2329f9ca5..17895e22c107 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -63,11 +63,6 @@ netty-codec-http ${project.version} - - ${project.groupId} - netty-handler - ${project.version} - com.jcraft jzlib From 37484635cbccf0bf48f0e013bc639f44bcfb420a Mon Sep 17 00:00:00 2001 From: root Date: Mon, 21 Jan 2019 12:26:12 +0000 Subject: [PATCH 352/417] [maven-release-plugin] prepare release netty-4.1.33.Final --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 76 insertions(+), 72 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 5f45e81705de..b97afa885e8e 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-all diff --git a/bom/pom.xml b/bom/pom.xml index b57dc86b0d5c..34d6b7ab963d 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.33.Final-SNAPSHOT + 4.1.33.Final pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.33.Final @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec-dns - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec-haproxy - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec-http - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec-http2 - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec-memcache - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec-mqtt - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec-redis - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec-smtp - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec-socks - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec-stomp - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-codec-xml - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-common - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-dev-tools - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-handler - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-handler-proxy - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-resolver - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-resolver-dns - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-transport - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-transport-rxtx - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-transport-sctp - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-transport-udt - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-example - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-all - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-transport-native-unix-common - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-transport-native-unix-common - 4.1.33.Final-SNAPSHOT + 4.1.33.Final linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.33.Final-SNAPSHOT + 4.1.33.Final osx-x86_64 io.netty netty-transport-native-epoll - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-transport-native-epoll - 4.1.33.Final-SNAPSHOT + 4.1.33.Final linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.33.Final-SNAPSHOT + 4.1.33.Final io.netty netty-transport-native-kqueue - 4.1.33.Final-SNAPSHOT + 4.1.33.Final osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 1083fef393b8..3ea7bd875f6c 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 2e950d349b20..41933ec61205 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index dde688ba7c0c..9e5f5f791877 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index d82f7f534195..65c517e13edd 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 17895e22c107..695f3b8766fa 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 65b453c20028..40c2a681e1a4 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 65db75defc6e..1de30c7554ad 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index d3d1140f04da..bbad7d99e739 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index ca591e11ab8e..1cd05fde2091 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 8498e63d519f..d2c20ef31ee6 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index dc44509c3348..7f5e47d0c481 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index c5d130de35f5..54ef3c524a2e 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 846c14520d4e..01205ef986b0 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-codec diff --git a/common/pom.xml b/common/pom.xml index 2722f23dfd04..3d8aa9985912 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 176a6e16dbcb..41b54840ab98 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.33.Final-SNAPSHOT + 4.1.33.Final Netty/Dev-Tools @@ -50,4 +50,8 @@ + + + netty-4.1.33.Final + diff --git a/example/pom.xml b/example/pom.xml index 2f727ac3e310..aac14ac188da 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 07dce585a7ce..0dcad25f0173 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index 1be8fad02f0a..4bddf4c9f2bc 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index e16df5d0ba95..f2ce65a4bda9 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-microbench diff --git a/pom.xml b/pom.xml index cc130091c6c2..60aa19da924d 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.33.Final-SNAPSHOT + 4.1.33.Final Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.33.Final diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index bd4237ea5cca..1f81cca4a014 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index 5546169a631b..aaa891a64ab1 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index a36d3a444bbc..d55b67334779 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 26bf5ce6f3db..c85f7e1fcab2 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index b13a739ae665..061cd944ef71 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 01a39284422c..767d8017f3c9 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index a0d615b37d70..890105e0b64b 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 548e78b80f8d..366f620a106e 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 3033703c0623..4f1f3b87719f 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 8e8c2c5b77b6..3efef19c9bce 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 0759ac556d5d..79a2317aeb58 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index ffa4ba90e1f6..586a004a9094 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index 3d54aabb6387..b1cfd0b485e1 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 94a04c7dc945..f1e1c4ff8ffc 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 07b03388acdc..628643d2e6e5 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 6075b13505f4..e2b4f8ea73b3 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final-SNAPSHOT + 4.1.33.Final netty-transport From cf03ed0478c12a7ed400dc00670351d46e0c48e3 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 21 Jan 2019 12:26:44 +0000 Subject: [PATCH 353/417] [maven-release-plugin] prepare for next development iteration --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 72 insertions(+), 76 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index b97afa885e8e..a9030dd9f5e0 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 34d6b7ab963d..3e3c3eb2e2a2 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.33.Final + 4.1.34.Final-SNAPSHOT pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.33.Final + HEAD @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec-dns - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec-haproxy - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec-http - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec-http2 - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec-memcache - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec-mqtt - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec-redis - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec-smtp - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec-socks - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec-stomp - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-codec-xml - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-common - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-dev-tools - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-handler - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-handler-proxy - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-resolver - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-resolver-dns - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-transport - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-transport-rxtx - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-transport-sctp - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-transport-udt - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-example - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-all - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-transport-native-unix-common - 4.1.33.Final + 4.1.34.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.33.Final + 4.1.34.Final-SNAPSHOT osx-x86_64 io.netty netty-transport-native-epoll - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-transport-native-epoll - 4.1.33.Final + 4.1.34.Final-SNAPSHOT linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.33.Final + 4.1.34.Final-SNAPSHOT io.netty netty-transport-native-kqueue - 4.1.33.Final + 4.1.34.Final-SNAPSHOT osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 3ea7bd875f6c..54227975158b 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 41933ec61205..8cee4876ef92 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 9e5f5f791877..2a339a631747 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 65c517e13edd..79d6b4ae34aa 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 695f3b8766fa..5dfd308433c6 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 40c2a681e1a4..c8f4a83a2be2 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 1de30c7554ad..d56c5ce645b9 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index bbad7d99e739..2f652f6d080f 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index 1cd05fde2091..98f3e2825005 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index d2c20ef31ee6..02fdf797faa2 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 7f5e47d0c481..ad2c54088748 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index 54ef3c524a2e..e54653b437c0 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 01205ef986b0..51a28a5452f1 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-codec diff --git a/common/pom.xml b/common/pom.xml index 3d8aa9985912..95821508b857 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 41b54840ab98..4a61abebf5e8 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.33.Final + 4.1.34.Final-SNAPSHOT Netty/Dev-Tools @@ -50,8 +50,4 @@ - - - netty-4.1.33.Final - diff --git a/example/pom.xml b/example/pom.xml index aac14ac188da..89b2798e1241 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index 0dcad25f0173..b3e553a4c6bc 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index 4bddf4c9f2bc..134e41cfd49f 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index f2ce65a4bda9..0cfea6172273 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-microbench diff --git a/pom.xml b/pom.xml index 60aa19da924d..c59d4335ceea 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.33.Final + 4.1.34.Final-SNAPSHOT Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.33.Final + HEAD diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index 1f81cca4a014..3faa65a80171 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index aaa891a64ab1..8bda0b96947e 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index d55b67334779..210290bf3dab 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index c85f7e1fcab2..b8a4fddc8c4c 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 061cd944ef71..409e2ed6aa01 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 767d8017f3c9..d484d29783cb 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index 890105e0b64b..c97075b32e67 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 366f620a106e..7d95d8f2f9b3 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 4f1f3b87719f..32cd9de959eb 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 3efef19c9bce..51a2d98ea207 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 79a2317aeb58..2345715e01ac 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 586a004a9094..cf7d2ed05771 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index b1cfd0b485e1..0188e8673c62 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index f1e1c4ff8ffc..1f098ed03fa5 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 628643d2e6e5..2b54e728014e 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index e2b4f8ea73b3..afab6a66e767 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.33.Final + 4.1.34.Final-SNAPSHOT netty-transport From 0431368621e16160801f226b1f95834d22448b46 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Tue, 22 Jan 2019 08:49:43 +0100 Subject: [PATCH 354/417] HttpUtil#is100ContinueExpected clean up (#8740) Motivation: Current implementation extract header value as String. We have an idiomatic way for checking presence of a header value. Modification: Use HttpHeaders#contains for checking if if contains Expect: 100-continue. Result: Use idiomatic way + simplify boolean logic. --- .../java/io/netty/handler/codec/http/HttpUtil.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java index 94af7901fc47..8e04b10db62a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java @@ -251,13 +251,9 @@ public static boolean isContentLengthSet(HttpMessage m) { * present */ public static boolean is100ContinueExpected(HttpMessage message) { - if (!isExpectHeaderValid(message)) { - return false; - } - - final String expectValue = message.headers().get(HttpHeaderNames.EXPECT); - // unquoted tokens in the expect header are case-insensitive, thus 100-continue is case insensitive - return HttpHeaderValues.CONTINUE.toString().equalsIgnoreCase(expectValue); + return isExpectHeaderValid(message) + // unquoted tokens in the expect header are case-insensitive, thus 100-continue is case insensitive + && message.headers().contains(HttpHeaderNames.EXPECT, HttpHeaderValues.CONTINUE, true); } /** From 57012dddb45b59a656dd523089ab0efdd204c452 Mon Sep 17 00:00:00 2001 From: kezhenxu94 Date: Tue, 22 Jan 2019 15:51:31 +0800 Subject: [PATCH 355/417] fix typo (#8741) Motivation: Correct typo Modification: Correct typo Result: JavaDoc and method name are more readable --- .../io/netty/util/concurrent/SingleThreadEventExecutor.java | 2 +- .../io/netty/util/concurrent/SingleThreadEventExecutorTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java index ab2a914c694b..03ae3347aeed 100644 --- a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java +++ b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java @@ -813,7 +813,7 @@ private void throwIfInEventLoop(String method) { /** * Returns the {@link ThreadProperties} of the {@link Thread} that powers the {@link SingleThreadEventExecutor}. - * If the {@link SingleThreadEventExecutor} is not started yet, this operation will start it and block until the + * If the {@link SingleThreadEventExecutor} is not started yet, this operation will start it and block until * it is fully started. */ public final ThreadProperties threadProperties() { diff --git a/common/src/test/java/io/netty/util/concurrent/SingleThreadEventExecutorTest.java b/common/src/test/java/io/netty/util/concurrent/SingleThreadEventExecutorTest.java index 55981b24060a..efb0eb015ea3 100644 --- a/common/src/test/java/io/netty/util/concurrent/SingleThreadEventExecutorTest.java +++ b/common/src/test/java/io/netty/util/concurrent/SingleThreadEventExecutorTest.java @@ -31,7 +31,7 @@ public class SingleThreadEventExecutorTest { @Test - public void testWrappedExecutureIsShutdown() { + public void testWrappedExecutorIsShutdown() { ExecutorService executorService = Executors.newSingleThreadExecutor(); SingleThreadEventExecutor executor = new SingleThreadEventExecutor(null, executorService, false) { From 3c2b86303a56725de052d2269aea3d6394177baf Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 24 Jan 2019 07:43:04 +0100 Subject: [PATCH 356/417] Release message when validation of passed in ChannelPromise fails when calling write(...) / writeAndFlush(...) (#8769) Motivation: We need to release the message when we throw an IllegalArgumentException because of a validation failure of the promise to eliminate the risk of a memory leak. Modifications: - Consistently release the message before rethrow - Add testcase. Result: Fixes https://github.com/netty/netty/issues/8765. --- .../AbstractChannelHandlerContext.java | 37 +++++---------- .../channel/DefaultChannelPipelineTest.java | 45 +++++++++++++++++++ 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java index 9e599c335d8b..6abf529afcc0 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java @@ -706,20 +706,6 @@ public ChannelFuture write(Object msg) { @Override public ChannelFuture write(final Object msg, final ChannelPromise promise) { - if (msg == null) { - throw new NullPointerException("msg"); - } - - try { - if (isNotValidPromise(promise, true)) { - ReferenceCountUtil.release(msg); - // cancelled - return promise; - } - } catch (RuntimeException e) { - ReferenceCountUtil.release(msg); - throw e; - } write(msg, false, promise); return promise; @@ -781,18 +767,7 @@ private void invokeFlush0() { @Override public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { - if (msg == null) { - throw new NullPointerException("msg"); - } - - if (isNotValidPromise(promise, true)) { - ReferenceCountUtil.release(msg); - // cancelled - return promise; - } - write(msg, true, promise); - return promise; } @@ -806,6 +781,18 @@ private void invokeWriteAndFlush(Object msg, ChannelPromise promise) { } private void write(Object msg, boolean flush, ChannelPromise promise) { + ObjectUtil.checkNotNull(msg, "msg"); + try { + if (isNotValidPromise(promise, true)) { + ReferenceCountUtil.release(msg); + // cancelled + return; + } + } catch (RuntimeException e) { + ReferenceCountUtil.release(msg); + throw e; + } + AbstractChannelHandlerContext next = findContextOutbound(); final Object m = pipeline.touch(msg, next); EventExecutor executor = next.executor(); diff --git a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java index 31de253b04e7..65209d50cfea 100644 --- a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java +++ b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java @@ -1223,6 +1223,51 @@ public void run() { } } + @Test + public void testWriteThrowsReleaseMessage() { + testWriteThrowsReleaseMessage0(false); + } + + @Test + public void testWriteAndFlushThrowsReleaseMessage() { + testWriteThrowsReleaseMessage0(true); + } + + private void testWriteThrowsReleaseMessage0(boolean flush) { + ReferenceCounted referenceCounted = new AbstractReferenceCounted() { + @Override + protected void deallocate() { + // NOOP + } + + @Override + public ReferenceCounted touch(Object hint) { + return this; + } + }; + assertEquals(1, referenceCounted.refCnt()); + + Channel channel = new LocalChannel(); + Channel channel2 = new LocalChannel(); + group.register(channel).syncUninterruptibly(); + group.register(channel2).syncUninterruptibly(); + + try { + if (flush) { + channel.writeAndFlush(referenceCounted, channel2.newPromise()); + } else { + channel.write(referenceCounted, channel2.newPromise()); + } + fail(); + } catch (IllegalArgumentException expected) { + // expected + } + assertEquals(0, referenceCounted.refCnt()); + + channel.close().syncUninterruptibly(); + channel2.close().syncUninterruptibly(); + } + @Test(timeout = 5000) public void handlerAddedStateUpdatedBeforeHandlerAddedDoneForceEventLoop() throws InterruptedException { handlerAddedStateUpdatedBeforeHandlerAddedDone(true); From 1d5b7be3a7e01b506f0b56cd58902a14fe7d9025 Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Thu, 24 Jan 2019 03:47:04 -0800 Subject: [PATCH 357/417] Fix three bugs in CompositeByteBuf (#8773) Motivation In #8758, @doom369 reported an infinite loop bug in CompositeByteBuf which was introduced in #8437. This is the same small fix for that, along with fixes for two other bugs found while re-inspecting the changes and adding unit tests. Modification - Replace recursive call to toComponentIndex with toComponentIndex0 as intended - Add missed "lastAccessed" racy cache invalidation in capacity(int) method - Fix incorrect determination of initial offset in non-zero cIndex case of updateComponentOffsets method - New unit tests for previously uncovered methods Results Fewer bugs. --- .../io/netty/buffer/CompositeByteBuf.java | 5 +- .../buffer/AbstractCompositeByteBufTest.java | 49 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 96c14cd28fee..43c12dc6bd7b 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -497,7 +497,7 @@ private void updateComponentOffsets(int cIndex) { return; } - int nextIndex = cIndex > 0 ? components[cIndex].endOffset : 0; + int nextIndex = cIndex > 0 ? components[cIndex - 1].endOffset : 0; for (; cIndex < size; cIndex++) { Component c = components[cIndex]; c.reposition(nextIndex); @@ -748,6 +748,7 @@ public CompositeByteBuf capacity(int newCapacity) { consolidateIfNeeded(); } } else if (newCapacity < oldCapacity) { + lastAccessed = null; int i = size - 1; for (int bytesToTrim = oldCapacity - newCapacity; i >= 0; i--) { Component c = components[i]; @@ -801,7 +802,7 @@ public int maxNumComponents() { */ public int toComponentIndex(int offset) { checkIndex(offset); - return toComponentIndex(offset); + return toComponentIndex0(offset); } private int toComponentIndex0(int offset) { diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java index c51a991ec370..e6bebe43cec6 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java @@ -135,6 +135,41 @@ public void testComponentAtOffset() { buf.release(); } + @Test + public void testToComponentIndex() { + CompositeByteBuf buf = (CompositeByteBuf) wrappedBuffer(new byte[]{1, 2, 3, 4, 5}, + new byte[]{4, 5, 6, 7, 8, 9, 26}, new byte[]{10, 9, 8, 7, 6, 5, 33}); + + // spot checks + assertEquals(0, buf.toComponentIndex(4)); + assertEquals(1, buf.toComponentIndex(5)); + assertEquals(2, buf.toComponentIndex(15)); + + //Loop through each byte + + byte index = 0; + + while (index < buf.capacity()) { + int cindex = buf.toComponentIndex(index++); + assertTrue(cindex >= 0 && cindex < buf.numComponents()); + } + + buf.release(); + } + + @Test + public void testToByteIndex() { + CompositeByteBuf buf = (CompositeByteBuf) wrappedBuffer(new byte[]{1, 2, 3, 4, 5}, + new byte[]{4, 5, 6, 7, 8, 9, 26}, new byte[]{10, 9, 8, 7, 6, 5, 33}); + + // spot checks + assertEquals(0, buf.toByteIndex(0)); + assertEquals(5, buf.toByteIndex(1)); + assertEquals(12, buf.toByteIndex(2)); + + buf.release(); + } + @Test public void testDiscardReadBytes3() { ByteBuf a, b; @@ -747,6 +782,20 @@ public void testRemoveLastComponentWithOthersLeft() { buf.release(); } + @Test + public void testRemoveComponents() { + CompositeByteBuf buf = compositeBuffer(); + for (int i = 0; i < 10; i++) { + buf.addComponent(wrappedBuffer(new byte[]{1, 2})); + } + assertEquals(10, buf.numComponents()); + assertEquals(20, buf.capacity()); + buf.removeComponents(4, 3); + assertEquals(7, buf.numComponents()); + assertEquals(14, buf.capacity()); + buf.release(); + } + @Test public void testGatheringWritesHeap() throws Exception { testGatheringWrites(buffer().order(order), buffer().order(order)); From cd3254df88b60476dc04b39915d3d70c200eb6f4 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 25 Jan 2019 11:58:42 +0100 Subject: [PATCH 358/417] Update to new checkstyle plugin (#8777) (#8780) Motivation: We need to update to a new checkstyle plugin to allow the usage of lambdas. Modifications: - Update to new plugin version. - Fix checkstyle problems. Result: Be able to use checkstyle plugin which supports new Java syntax. --- .../buffer/PooledByteBufAllocatorTest.java | 2 +- .../codec/http/CombinedHttpHeaders.java | 2 +- .../handler/codec/http/HttpServerCodec.java | 8 ++++-- .../http/multipart/AbstractHttpData.java | 4 ++- .../WebSocketClientProtocolHandler.java | 4 ++- .../compression/DeflateDecoder.java | 2 +- .../compression/DeflateEncoder.java | 2 +- ...DeflateFrameClientExtensionHandshaker.java | 2 +- ...DeflateFrameServerExtensionHandshaker.java | 2 +- .../compression/PerFrameDeflateDecoder.java | 2 +- .../compression/PerFrameDeflateEncoder.java | 2 +- ...ssageDeflateClientExtensionHandshaker.java | 2 +- .../compression/PerMessageDeflateDecoder.java | 2 +- .../compression/PerMessageDeflateEncoder.java | 2 +- ...ssageDeflateServerExtensionHandshaker.java | 2 +- .../WebSocketExtensionTestUtil.java | 2 +- .../codec/http2/DefaultHttp2Connection.java | 2 +- .../DefaultHttp2LocalFlowController.java | 6 ++-- .../handler/codec/http2/HpackDecoder.java | 2 +- .../handler/codec/http2/HpackEncoder.java | 4 +-- .../codec/http2/Http2ConnectionHandler.java | 2 +- .../handler/codec/http2/Http2Exception.java | 2 +- .../http2/DefaultHttp2ConnectionTest.java | 4 +-- .../netty/handler/codec/xml/XmlAttribute.java | 28 ++++++++++++++----- .../netty/handler/codec/xml/XmlContent.java | 12 ++++++-- .../io/netty/handler/codec/xml/XmlDTD.java | 12 ++++++-- .../handler/codec/xml/XmlDocumentStart.java | 20 +++++++++---- .../netty/handler/codec/xml/XmlElement.java | 24 ++++++++++++---- .../handler/codec/xml/XmlElementStart.java | 16 ++++++++--- .../handler/codec/xml/XmlEntityReference.java | 16 ++++++++--- .../netty/handler/codec/xml/XmlNamespace.java | 16 ++++++++--- .../codec/xml/XmlProcessingInstruction.java | 16 ++++++++--- .../codec/compression/Bzip2DivSufSort.java | 4 ++- .../handler/codec/DefaultHeadersTest.java | 4 +-- .../main/java/io/netty/util/CharsetUtil.java | 4 ++- .../io/netty/util/ResourceLeakDetector.java | 5 ++-- .../netty/example/ocsp/OcspClientExample.java | 4 +-- .../netty/handler/proxy/ProxyHandlerTest.java | 4 ++- .../ssl/OpenSslJavaxX509Certificate.java | 2 +- .../handler/ssl/OpenSslX509Certificate.java | 2 +- .../java/io/netty/handler/ssl/PemValue.java | 2 +- .../io/netty/handler/ssl/SniHandlerTest.java | 2 +- .../io/netty/handler/ssl/SslErrorTest.java | 2 +- .../io/netty/handler/ssl/ocsp/OcspTest.java | 6 ++-- .../handler/timeout/IdleStateHandlerTest.java | 4 +-- .../codec/http2/HpackBenchmarkUtil.java | 2 +- .../BurstCostExecutorsBenchmark.java | 2 +- pom.xml | 9 ++++-- transport-native-epoll/pom.xml | 1 - .../channel/epoll/AbstractEpollChannel.java | 4 ++- ...EpollRecvByteAllocatorStreamingHandle.java | 2 +- .../io/netty/channel/epoll/LinuxSocket.java | 2 +- .../netty/channel/epoll/EpollSpliceTest.java | 2 +- transport-native-kqueue/pom.xml | 3 -- .../channel/kqueue/AbstractKQueueChannel.java | 4 ++- .../channel/AdaptiveRecvByteBufAllocator.java | 2 +- .../channel/FixedRecvByteBufAllocator.java | 2 +- .../netty/channel/pool/FixedChannelPool.java | 2 +- .../io/netty/channel/AbstractChannelTest.java | 2 +- .../DefaultChannelPipelineTailTest.java | 4 +-- 60 files changed, 204 insertions(+), 108 deletions(-) diff --git a/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java b/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java index 495bb765406a..9116e9c73e22 100644 --- a/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java +++ b/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java @@ -461,7 +461,7 @@ private static final class AllocationThread extends Thread { private final ByteBufAllocator allocator; private final AtomicReference finish = new AtomicReference(); - public AllocationThread(ByteBufAllocator allocator) { + AllocationThread(ByteBufAllocator allocator) { this.allocator = allocator; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java index ae494934d7c5..b8a2d0b8915c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java @@ -79,7 +79,7 @@ public CharSequence escape(CharSequence value) { return charSequenceEscaper; } - public CombinedHttpHeadersImpl(HashingStrategy nameHashingStrategy, + CombinedHttpHeadersImpl(HashingStrategy nameHashingStrategy, ValueConverter valueConverter, io.netty.handler.codec.DefaultHeaders.NameValidator nameValidator) { super(nameHashingStrategy, valueConverter, nameValidator); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java index 4e8d61361b29..e2551dcee146 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java @@ -81,16 +81,18 @@ public void upgradeFrom(ChannelHandlerContext ctx) { } private final class HttpServerRequestDecoder extends HttpRequestDecoder { - public HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) { + + HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) { super(maxInitialLineLength, maxHeaderSize, maxChunkSize); } - public HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, + HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders) { super(maxInitialLineLength, maxHeaderSize, maxChunkSize, validateHeaders); } - public HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, + HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, + boolean validateHeaders, int initialBufferSize) { super(maxInitialLineLength, maxHeaderSize, maxChunkSize, validateHeaders, initialBufferSize); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractHttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractHttpData.java index ff05753bae53..6657f5f52743 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractHttpData.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractHttpData.java @@ -59,7 +59,9 @@ protected AbstractHttpData(String name, Charset charset, long size) { } @Override - public long getMaxSize() { return maxSize; } + public long getMaxSize() { + return maxSize; + } @Override public void setMaxSize(long maxSize) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java index 7c85c57748df..a40e2d74e54e 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java @@ -45,7 +45,9 @@ public class WebSocketClientProtocolHandler extends WebSocketProtocolHandler { /** * Returns the used handshaker */ - public WebSocketClientHandshaker handshaker() { return handshaker; } + public WebSocketClientHandshaker handshaker() { + return handshaker; + } /** * Events that are fired to notify about handshake status diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateDecoder.java index 89053fd82d29..02320528725f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateDecoder.java @@ -47,7 +47,7 @@ abstract class DeflateDecoder extends WebSocketExtensionDecoder { * Constructor * @param noContext true to disable context takeover. */ - public DeflateDecoder(boolean noContext) { + DeflateDecoder(boolean noContext) { this.noContext = noContext; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateEncoder.java index 1aff7b78c7a9..b70c06bc3c21 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateEncoder.java @@ -49,7 +49,7 @@ abstract class DeflateEncoder extends WebSocketExtensionEncoder { * @param windowSize maximum size of the window compressor buffer. * @param noContext true to disable context takeover. */ - public DeflateEncoder(int compressionLevel, int windowSize, boolean noContext) { + DeflateEncoder(int compressionLevel, int windowSize, boolean noContext) { this.compressionLevel = compressionLevel; this.windowSize = windowSize; this.noContext = noContext; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameClientExtensionHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameClientExtensionHandshaker.java index 6671d1f1af8b..c167b375e4be 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameClientExtensionHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameClientExtensionHandshaker.java @@ -81,7 +81,7 @@ private static class DeflateFrameClientExtension implements WebSocketClientExten private final int compressionLevel; - public DeflateFrameClientExtension(int compressionLevel) { + DeflateFrameClientExtension(int compressionLevel) { this.compressionLevel = compressionLevel; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameServerExtensionHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameServerExtensionHandshaker.java index e7ea9f3571b8..a463fbb755fb 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameServerExtensionHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameServerExtensionHandshaker.java @@ -74,7 +74,7 @@ private static class DeflateFrameServerExtension implements WebSocketServerExten private final String extensionName; private final int compressionLevel; - public DeflateFrameServerExtension(int compressionLevel, String extensionName) { + DeflateFrameServerExtension(int compressionLevel, String extensionName) { this.extensionName = extensionName; this.compressionLevel = compressionLevel; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateDecoder.java index ad9554425079..b80b55e73036 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateDecoder.java @@ -30,7 +30,7 @@ class PerFrameDeflateDecoder extends DeflateDecoder { * Constructor * @param noContext true to disable context takeover. */ - public PerFrameDeflateDecoder(boolean noContext) { + PerFrameDeflateDecoder(boolean noContext) { super(noContext); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateEncoder.java index aaffd8dc03e6..acee48bd3cde 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateEncoder.java @@ -32,7 +32,7 @@ class PerFrameDeflateEncoder extends DeflateEncoder { * @param windowSize maximum size of the window compressor buffer. * @param noContext true to disable context takeover. */ - public PerFrameDeflateEncoder(int compressionLevel, int windowSize, boolean noContext) { + PerFrameDeflateEncoder(int compressionLevel, int windowSize, boolean noContext) { super(compressionLevel, windowSize, noContext); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshaker.java index ddebaaa92f04..443d6299c555 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshaker.java @@ -176,7 +176,7 @@ public int rsv() { return RSV1; } - public PermessageDeflateExtension(boolean serverNoContext, int serverWindowSize, + PermessageDeflateExtension(boolean serverNoContext, int serverWindowSize, boolean clientNoContext, int clientWindowSize) { this.serverNoContext = serverNoContext; this.serverWindowSize = serverWindowSize; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateDecoder.java index a69294ed22a8..bfe0deb2cc0d 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateDecoder.java @@ -35,7 +35,7 @@ class PerMessageDeflateDecoder extends DeflateDecoder { * Constructor * @param noContext true to disable context takeover. */ - public PerMessageDeflateDecoder(boolean noContext) { + PerMessageDeflateDecoder(boolean noContext) { super(noContext); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateEncoder.java index b1cdc660fef9..7c9ff0a4a0db 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateEncoder.java @@ -37,7 +37,7 @@ class PerMessageDeflateEncoder extends DeflateEncoder { * @param windowSize maximum size of the window compressor buffer. * @param noContext true to disable context takeover. */ - public PerMessageDeflateEncoder(int compressionLevel, int windowSize, boolean noContext) { + PerMessageDeflateEncoder(int compressionLevel, int windowSize, boolean noContext) { super(compressionLevel, windowSize, noContext); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshaker.java index 0bf0162e8a0b..3aa8397d2181 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshaker.java @@ -151,7 +151,7 @@ private static class PermessageDeflateExtension implements WebSocketServerExtens private final boolean clientNoContext; private final int clientWindowSize; - public PermessageDeflateExtension(int compressionLevel, boolean serverNoContext, + PermessageDeflateExtension(int compressionLevel, boolean serverNoContext, int serverWindowSize, boolean clientNoContext, int clientWindowSize) { this.compressionLevel = compressionLevel; this.serverNoContext = serverNoContext; diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionTestUtil.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionTestUtil.java index 411b167f8f3c..38867febd100 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionTestUtil.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionTestUtil.java @@ -69,7 +69,7 @@ static final class WebSocketExtensionDataMatcher implements ArgumentMatcher streams = new LinkedHashSet(); private int pendingIterations; - public ActiveStreams(List listeners) { + ActiveStreams(List listeners) { this.listeners = listeners; } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java index 74dc3ae31c44..9c8308ecf3d3 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java @@ -296,7 +296,7 @@ private static boolean isClosed(Http2Stream stream) { * received. */ private final class AutoRefillState extends DefaultState { - public AutoRefillState(Http2Stream stream, int initialWindowSize) { + AutoRefillState(Http2Stream stream, int initialWindowSize) { super(stream, initialWindowSize); } @@ -349,7 +349,7 @@ private class DefaultState implements FlowState { private int lowerBound; private boolean endOfStream; - public DefaultState(Http2Stream stream, int initialWindowSize) { + DefaultState(Http2Stream stream, int initialWindowSize) { this.stream = stream; window(initialWindowSize); streamWindowUpdateRatio = windowUpdateRatio; @@ -613,7 +613,7 @@ private final class WindowUpdateVisitor implements Http2StreamVisitor { private CompositeStreamException compositeException; private final int delta; - public WindowUpdateVisitor(int delta) { + WindowUpdateVisitor(int delta) { this.delta = delta; } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java index 9c680e6a99e0..389fc0483629 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java @@ -528,7 +528,7 @@ private static final class Http2HeadersSink implements Sink { private HeaderType previousType; private Http2Exception validationException; - public Http2HeadersSink(int streamId, Http2Headers headers, long maxHeaderListSize, boolean validate) { + Http2HeadersSink(int streamId, Http2Headers headers, long maxHeaderListSize, boolean validate) { this.headers = headers; this.maxHeaderListSize = maxHeaderListSize; this.streamId = streamId; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackEncoder.java index 7719072a499c..301a2c51cfb8 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackEncoder.java @@ -75,14 +75,14 @@ final class HpackEncoder { /** * Creates a new encoder. */ - public HpackEncoder(boolean ignoreMaxHeaderListSize) { + HpackEncoder(boolean ignoreMaxHeaderListSize) { this(ignoreMaxHeaderListSize, 16); } /** * Creates a new encoder. */ - public HpackEncoder(boolean ignoreMaxHeaderListSize, int arraySizeHint) { + HpackEncoder(boolean ignoreMaxHeaderListSize, int arraySizeHint) { this.ignoreMaxHeaderListSize = ignoreMaxHeaderListSize; maxHeaderTableSize = DEFAULT_HEADER_TABLE_SIZE; maxHeaderListSize = MAX_HEADER_LIST_SIZE; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java index 4f66e918d54e..618a4a6771bd 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java @@ -233,7 +233,7 @@ private final class PrefaceDecoder extends BaseDecoder { private ByteBuf clientPrefaceString; private boolean prefaceSent; - public PrefaceDecoder(ChannelHandlerContext ctx) throws Exception { + PrefaceDecoder(ChannelHandlerContext ctx) throws Exception { clientPrefaceString = clientPrefaceString(encoder.connection()); // This handler was just added to the context. In case it was handled after // the connection became active, send the connection preface now. diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Exception.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Exception.java index 258f871eb840..c41600109322 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Exception.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Exception.java @@ -194,7 +194,7 @@ public static int streamId(Http2Exception e) { /** * Provides a hint as to if shutdown is justified, what type of shutdown should be executed. */ - public static enum ShutdownHint { + public enum ShutdownHint { /** * Do not shutdown the underlying channel. */ diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java index 69183e0843d8..b43c00020670 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java @@ -624,7 +624,7 @@ private static final class ListenerExceptionThrower implements Answer { private final boolean[] array; private final int index; - public ListenerExceptionThrower(boolean[] array, int index) { + ListenerExceptionThrower(boolean[] array, int index) { this.array = array; this.index = index; } @@ -640,7 +640,7 @@ private static final class ListenerVerifyCallAnswer implements Answer { private final boolean[] array; private final int index; - public ListenerVerifyCallAnswer(boolean[] array, int index) { + ListenerVerifyCallAnswer(boolean[] array, int index) { this.array = array; this.index = index; } diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlAttribute.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlAttribute.java index d40ce138c339..968d306c3069 100644 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlAttribute.java +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlAttribute.java @@ -57,16 +57,30 @@ public String value() { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } XmlAttribute that = (XmlAttribute) o; - if (!name.equals(that.name)) { return false; } - if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) { return false; } - if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { return false; } - if (type != null ? !type.equals(that.type) : that.type != null) { return false; } - if (value != null ? !value.equals(that.value) : that.value != null) { return false; } + if (!name.equals(that.name)) { + return false; + } + if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) { + return false; + } + if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { + return false; + } + if (type != null ? !type.equals(that.type) : that.type != null) { + return false; + } + if (value != null ? !value.equals(that.value) : that.value != null) { + return false; + } return true; } diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlContent.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlContent.java index a47df3312c86..275297c64147 100644 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlContent.java +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlContent.java @@ -32,12 +32,18 @@ public String data() { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } XmlContent that = (XmlContent) o; - if (data != null ? !data.equals(that.data) : that.data != null) { return false; } + if (data != null ? !data.equals(that.data) : that.data != null) { + return false; + } return true; } diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDTD.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDTD.java index 754539b243ca..e36648f55eac 100644 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDTD.java +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDTD.java @@ -32,12 +32,18 @@ public String text() { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } XmlDTD xmlDTD = (XmlDTD) o; - if (text != null ? !text.equals(xmlDTD.text) : xmlDTD.text != null) { return false; } + if (text != null ? !text.equals(xmlDTD.text) : xmlDTD.text != null) { + return false; + } return true; } diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentStart.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentStart.java index 311a1f5702c3..98ce875cf164 100644 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentStart.java +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentStart.java @@ -54,17 +54,27 @@ public String encodingScheme() { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } XmlDocumentStart that = (XmlDocumentStart) o; - if (standalone != that.standalone) { return false; } - if (encoding != null ? !encoding.equals(that.encoding) : that.encoding != null) { return false; } + if (standalone != that.standalone) { + return false; + } + if (encoding != null ? !encoding.equals(that.encoding) : that.encoding != null) { + return false; + } if (encodingScheme != null ? !encodingScheme.equals(that.encodingScheme) : that.encodingScheme != null) { return false; } - if (version != null ? !version.equals(that.version) : that.version != null) { return false; } + if (version != null ? !version.equals(that.version) : that.version != null) { + return false; + } return true; } diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElement.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElement.java index 885e814a098c..8391bd09ee64 100644 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElement.java +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElement.java @@ -54,15 +54,27 @@ public List namespaces() { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } XmlElement that = (XmlElement) o; - if (!name.equals(that.name)) { return false; } - if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) { return false; } - if (namespaces != null ? !namespaces.equals(that.namespaces) : that.namespaces != null) { return false; } - if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { return false; } + if (!name.equals(that.name)) { + return false; + } + if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) { + return false; + } + if (namespaces != null ? !namespaces.equals(that.namespaces) : that.namespaces != null) { + return false; + } + if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { + return false; + } return true; } diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementStart.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementStart.java index 17d603eb4caf..19024230c896 100644 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementStart.java +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementStart.java @@ -35,13 +35,21 @@ public List attributes() { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } - if (!super.equals(o)) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } XmlElementStart that = (XmlElementStart) o; - if (attributes != null ? !attributes.equals(that.attributes) : that.attributes != null) { return false; } + if (attributes != null ? !attributes.equals(that.attributes) : that.attributes != null) { + return false; + } return true; } diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlEntityReference.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlEntityReference.java index ba3cba9adad7..78ed9e709e02 100644 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlEntityReference.java +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlEntityReference.java @@ -38,13 +38,21 @@ public String text() { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } XmlEntityReference that = (XmlEntityReference) o; - if (name != null ? !name.equals(that.name) : that.name != null) { return false; } - if (text != null ? !text.equals(that.text) : that.text != null) { return false; } + if (name != null ? !name.equals(that.name) : that.name != null) { + return false; + } + if (text != null ? !text.equals(that.text) : that.text != null) { + return false; + } return true; } diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlNamespace.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlNamespace.java index 9cbb86fb42c5..2d0ae5696ba0 100644 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlNamespace.java +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlNamespace.java @@ -38,13 +38,21 @@ public String uri() { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } XmlNamespace that = (XmlNamespace) o; - if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { return false; } - if (uri != null ? !uri.equals(that.uri) : that.uri != null) { return false; } + if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { + return false; + } + if (uri != null ? !uri.equals(that.uri) : that.uri != null) { + return false; + } return true; } diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlProcessingInstruction.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlProcessingInstruction.java index 27dc4deee0d2..6f7588062cde 100644 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlProcessingInstruction.java +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlProcessingInstruction.java @@ -38,13 +38,21 @@ public String target() { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } XmlProcessingInstruction that = (XmlProcessingInstruction) o; - if (data != null ? !data.equals(that.data) : that.data != null) { return false; } - if (target != null ? !target.equals(that.target) : that.target != null) { return false; } + if (data != null ? !data.equals(that.data) : that.data != null) { + return false; + } + if (target != null ? !target.equals(that.target) : that.target != null) { + return false; + } return true; } diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2DivSufSort.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2DivSufSort.java index cdf92a698313..813872938c2c 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2DivSufSort.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2DivSufSort.java @@ -568,7 +568,9 @@ private void ssMergeForward(final int pa, int[] buf, final int bufoffset, SA[i++] = SA[k]; SA[k++] = SA[i]; if (last <= k) { - while (j < bufend) { SA[i++] = buf[j]; buf[j++] = SA[i]; } + while (j < bufend) { + SA[i++] = buf[j]; buf[j++] = SA[i]; + } SA[i] = buf[j]; buf[j] = t; return; } diff --git a/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java b/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java index 073872bf10c5..2ed0498be4ba 100644 --- a/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java +++ b/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java @@ -41,11 +41,11 @@ public class DefaultHeadersTest { private static final class TestDefaultHeaders extends DefaultHeaders { - public TestDefaultHeaders() { + TestDefaultHeaders() { this(CharSequenceValueConverter.INSTANCE); } - public TestDefaultHeaders(ValueConverter converter) { + TestDefaultHeaders(ValueConverter converter) { super(converter); } } diff --git a/common/src/main/java/io/netty/util/CharsetUtil.java b/common/src/main/java/io/netty/util/CharsetUtil.java index 4d71b0a7082b..a9317e5497b0 100644 --- a/common/src/main/java/io/netty/util/CharsetUtil.java +++ b/common/src/main/java/io/netty/util/CharsetUtil.java @@ -65,7 +65,9 @@ public final class CharsetUtil { private static final Charset[] CHARSETS = new Charset[] { UTF_16, UTF_16BE, UTF_16LE, UTF_8, ISO_8859_1, US_ASCII }; - public static Charset[] values() { return CHARSETS; } + public static Charset[] values() { + return CHARSETS; + } /** * @deprecated Use {@link #encoder(Charset)}. diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetector.java b/common/src/main/java/io/netty/util/ResourceLeakDetector.java index 436d793669ff..e7fc140c67c6 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetector.java +++ b/common/src/main/java/io/netty/util/ResourceLeakDetector.java @@ -498,8 +498,9 @@ public boolean close(T trackedObject) { */ private static void reachabilityFence0(Object ref) { if (ref != null) { - // Empty synchronized is ok: https://stackoverflow.com/a/31933260/1151521 - synchronized (ref) { } + synchronized (ref) { + // Empty synchronized is ok: https://stackoverflow.com/a/31933260/1151521 + } } } diff --git a/example/src/main/java/io/netty/example/ocsp/OcspClientExample.java b/example/src/main/java/io/netty/example/ocsp/OcspClientExample.java index f36001cab43c..814e44363d8a 100644 --- a/example/src/main/java/io/netty/example/ocsp/OcspClientExample.java +++ b/example/src/main/java/io/netty/example/ocsp/OcspClientExample.java @@ -157,7 +157,7 @@ private static class HttpClientHandler extends ChannelInboundHandlerAdapter { private final Promise promise; - public HttpClientHandler(String host, Promise promise) { + HttpClientHandler(String host, Promise promise) { this.host = host; this.promise = promise; } @@ -203,7 +203,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E private static class ExampleOcspClientHandler extends OcspClientHandler { - public ExampleOcspClientHandler(ReferenceCountedOpenSslEngine engine) { + ExampleOcspClientHandler(ReferenceCountedOpenSslEngine engine) { super(engine); } diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java b/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java index 6f0f325d641a..77e20fb864c2 100644 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java +++ b/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java @@ -431,7 +431,9 @@ public static void stopServers() { private final TestItem testItem; - public ProxyHandlerTest(TestItem testItem) { this.testItem = testItem; } + public ProxyHandlerTest(TestItem testItem) { + this.testItem = testItem; + } @Before public void clearServerExceptions() throws Exception { diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslJavaxX509Certificate.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslJavaxX509Certificate.java index da10dedf0c1a..af52ddc92ae4 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslJavaxX509Certificate.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslJavaxX509Certificate.java @@ -32,7 +32,7 @@ final class OpenSslJavaxX509Certificate extends X509Certificate { private final byte[] bytes; private X509Certificate wrapped; - public OpenSslJavaxX509Certificate(byte[] bytes) { + OpenSslJavaxX509Certificate(byte[] bytes) { this.bytes = bytes; } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509Certificate.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509Certificate.java index b94a19577be2..52dbbdfa8d64 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509Certificate.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509Certificate.java @@ -41,7 +41,7 @@ final class OpenSslX509Certificate extends X509Certificate { private final byte[] bytes; private X509Certificate wrapped; - public OpenSslX509Certificate(byte[] bytes) { + OpenSslX509Certificate(byte[] bytes) { this.bytes = bytes; } diff --git a/handler/src/main/java/io/netty/handler/ssl/PemValue.java b/handler/src/main/java/io/netty/handler/ssl/PemValue.java index becb5b849215..ada5e4ddf661 100644 --- a/handler/src/main/java/io/netty/handler/ssl/PemValue.java +++ b/handler/src/main/java/io/netty/handler/ssl/PemValue.java @@ -34,7 +34,7 @@ class PemValue extends AbstractReferenceCounted implements PemEncoded { private final boolean sensitive; - public PemValue(ByteBuf content, boolean sensitive) { + PemValue(ByteBuf content, boolean sensitive) { this.content = ObjectUtil.checkNotNull(content, "content"); this.sensitive = sensitive; } diff --git a/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java index d3bf1f24285a..8d8003d745e1 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java @@ -542,7 +542,7 @@ protected void initChannel(Channel ch) throws Exception { private static class CustomSslHandler extends SslHandler { private final SslContext sslContext; - public CustomSslHandler(SslContext sslContext, SSLEngine sslEngine) { + CustomSslHandler(SslContext sslContext, SSLEngine sslEngine) { super(sslEngine); this.sslContext = ObjectUtil.checkNotNull(sslContext, "sslContext"); } diff --git a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java index 9f205825e55b..fbe31d73f7a1 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java @@ -262,7 +262,7 @@ private static void verifyException(Throwable cause, String messagePart, Promise private static final class TestCertificateException extends CertificateException { private static final long serialVersionUID = -5816338303868751410L; - public TestCertificateException(Throwable cause) { + TestCertificateException(Throwable cause) { super(cause); } } diff --git a/handler/src/test/java/io/netty/handler/ssl/ocsp/OcspTest.java b/handler/src/test/java/io/netty/handler/ssl/ocsp/OcspTest.java index 161f52a99f64..cfed8240463b 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ocsp/OcspTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ocsp/OcspTest.java @@ -459,7 +459,7 @@ private static final class TestClientOcspContext implements OcspClientCallback { private volatile byte[] response; - public TestClientOcspContext(boolean valid) { + TestClientOcspContext(boolean valid) { this.valid = valid; } @@ -481,7 +481,7 @@ private static final class OcspClientCallbackHandler extends OcspClientHandler { private final OcspClientCallback callback; - public OcspClientCallbackHandler(ReferenceCountedOpenSslEngine engine, OcspClientCallback callback) { + OcspClientCallbackHandler(ReferenceCountedOpenSslEngine engine, OcspClientCallback callback) { super(engine); this.callback = callback; } @@ -496,7 +496,7 @@ protected boolean verify(ChannelHandlerContext ctx, ReferenceCountedOpenSslEngin private static final class OcspTestException extends IllegalStateException { private static final long serialVersionUID = 4516426833250228159L; - public OcspTestException(String message) { + OcspTestException(String message) { super(message); } } diff --git a/handler/src/test/java/io/netty/handler/timeout/IdleStateHandlerTest.java b/handler/src/test/java/io/netty/handler/timeout/IdleStateHandlerTest.java index 0a5b6278224e..a27364f43933 100644 --- a/handler/src/test/java/io/netty/handler/timeout/IdleStateHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/timeout/IdleStateHandlerTest.java @@ -317,7 +317,7 @@ private static class TestableIdleStateHandler extends IdleStateHandler { private long ticksInNanos; - public TestableIdleStateHandler(boolean observeOutput, + TestableIdleStateHandler(boolean observeOutput, long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit) { super(observeOutput, readerIdleTime, writerIdleTime, allIdleTime, unit); @@ -369,7 +369,7 @@ ScheduledFuture schedule(ChannelHandlerContext ctx, Runnable task, long delay private static class ObservableChannel extends EmbeddedChannel { - public ObservableChannel(ChannelHandler... handlers) { + ObservableChannel(ChannelHandler... handlers) { super(handlers); } diff --git a/microbench/src/main/java/io/netty/handler/codec/http2/HpackBenchmarkUtil.java b/microbench/src/main/java/io/netty/handler/codec/http2/HpackBenchmarkUtil.java index c94bc59a8c75..e8a3a56d1ea8 100644 --- a/microbench/src/main/java/io/netty/handler/codec/http2/HpackBenchmarkUtil.java +++ b/microbench/src/main/java/io/netty/handler/codec/http2/HpackBenchmarkUtil.java @@ -49,7 +49,7 @@ private static class HeadersKey { final HpackHeadersSize size; final boolean limitToAscii; - public HeadersKey(HpackHeadersSize size, boolean limitToAscii) { + HeadersKey(HpackHeadersSize size, boolean limitToAscii) { this.size = size; this.limitToAscii = limitToAscii; } diff --git a/microbench/src/main/java/io/netty/microbench/concurrent/BurstCostExecutorsBenchmark.java b/microbench/src/main/java/io/netty/microbench/concurrent/BurstCostExecutorsBenchmark.java index 11164d2f0b20..acab0f033d4f 100644 --- a/microbench/src/main/java/io/netty/microbench/concurrent/BurstCostExecutorsBenchmark.java +++ b/microbench/src/main/java/io/netty/microbench/concurrent/BurstCostExecutorsBenchmark.java @@ -67,7 +67,7 @@ public void run() { private final AtomicBoolean poisoned = new AtomicBoolean(); private final Thread executorThread; - public SpinExecutorService(int maxTasks) { + SpinExecutorService(int maxTasks) { tasks = PlatformDependent.newFixedMpscQueue(maxTasks); executorThread = new Thread(new Runnable() { @Override diff --git a/pom.xml b/pom.xml index c59d4335ceea..015e52f098a9 100644 --- a/pom.xml +++ b/pom.xml @@ -253,7 +253,7 @@ ${project.build.directory}/dev-tools UTF-8 UTF-8 - 22 + 23 1.4.11.Final 2.0.8 "${settings.localRepository}"/org/mortbay/jetty/alpn/jetty-alpn-agent/${jetty.alpnAgent.version}/jetty-alpn-agent-${jetty.alpnAgent.version}.jar @@ -830,7 +830,7 @@ maven-checkstyle-plugin - 2.12.1 + 3.0.0 check-style @@ -844,7 +844,10 @@ true true io/netty/checkstyle.xml - true + + ${project.build.sourceDirectory} + ${project.build.testSourceDirectory} + diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 32cd9de959eb..e6eb48bbd7e0 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -161,7 +161,6 @@ generate build - compile diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java index 25ae95b2d4d2..41c7d2cda30b 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java @@ -388,7 +388,9 @@ public void run() { */ abstract void epollInReady(); - final void epollInBefore() { maybeMoreDataToRead = false; } + final void epollInBefore() { + maybeMoreDataToRead = false; + } final void epollInFinally(ChannelConfig config) { maybeMoreDataToRead = allocHandle.maybeMoreDataToRead(); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorStreamingHandle.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorStreamingHandle.java index f6ba5f58b54d..071acd578c8d 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorStreamingHandle.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollRecvByteAllocatorStreamingHandle.java @@ -18,7 +18,7 @@ import io.netty.channel.RecvByteBufAllocator; final class EpollRecvByteAllocatorStreamingHandle extends EpollRecvByteAllocatorHandle { - public EpollRecvByteAllocatorStreamingHandle(RecvByteBufAllocator.ExtendedHandle handle) { + EpollRecvByteAllocatorStreamingHandle(RecvByteBufAllocator.ExtendedHandle handle) { super(handle); } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java index d3578b4dae7e..a20dc4aa06a5 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java @@ -40,7 +40,7 @@ final class LinuxSocket extends Socket { private static final ClosedChannelException SENDFILE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace( new ClosedChannelException(), Native.class, "sendfile(...)"); - public LinuxSocket(int fd) { + LinuxSocket(int fd) { super(fd); } diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSpliceTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSpliceTest.java index c53ff1ea78a7..7e43db6fa23a 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSpliceTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSpliceTest.java @@ -296,7 +296,7 @@ private static class SpliceHandler extends ChannelInboundHandlerAdapter { volatile ChannelFuture future; final AtomicReference exception = new AtomicReference(); - public SpliceHandler(File file) { + SpliceHandler(File file) { this.file = file; } diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 51a2d98ea207..41a4db66c77b 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -90,7 +90,6 @@ generate build - compile @@ -198,7 +197,6 @@ generate build - compile @@ -305,7 +303,6 @@ generate build - compile diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java index 559999ce7eeb..be07852f3783 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java @@ -392,7 +392,9 @@ final void readReady(long numberBytesPending) { abstract void readReady(KQueueRecvByteAllocatorHandle allocHandle); - final void readReadyBefore() { maybeMoreDataToRead = false; } + final void readReadyBefore() { + maybeMoreDataToRead = false; + } final void readReadyFinally(ChannelConfig config) { maybeMoreDataToRead = allocHandle.maybeMoreDataToRead(); diff --git a/transport/src/main/java/io/netty/channel/AdaptiveRecvByteBufAllocator.java b/transport/src/main/java/io/netty/channel/AdaptiveRecvByteBufAllocator.java index a2db615f6bb4..e7227db82e34 100644 --- a/transport/src/main/java/io/netty/channel/AdaptiveRecvByteBufAllocator.java +++ b/transport/src/main/java/io/netty/channel/AdaptiveRecvByteBufAllocator.java @@ -95,7 +95,7 @@ private final class HandleImpl extends MaxMessageHandle { private int nextReceiveBufferSize; private boolean decreaseNow; - public HandleImpl(int minIndex, int maxIndex, int initial) { + HandleImpl(int minIndex, int maxIndex, int initial) { this.minIndex = minIndex; this.maxIndex = maxIndex; diff --git a/transport/src/main/java/io/netty/channel/FixedRecvByteBufAllocator.java b/transport/src/main/java/io/netty/channel/FixedRecvByteBufAllocator.java index 8dab77dad77f..bcc091ea7e2e 100644 --- a/transport/src/main/java/io/netty/channel/FixedRecvByteBufAllocator.java +++ b/transport/src/main/java/io/netty/channel/FixedRecvByteBufAllocator.java @@ -26,7 +26,7 @@ public class FixedRecvByteBufAllocator extends DefaultMaxMessagesRecvByteBufAllo private final class HandleImpl extends MaxMessageHandle { private final int bufferSize; - public HandleImpl(int bufferSize) { + HandleImpl(int bufferSize) { this.bufferSize = bufferSize; } diff --git a/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java b/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java index 5ca376f88d21..26ded639ef91 100644 --- a/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java +++ b/transport/src/main/java/io/netty/channel/pool/FixedChannelPool.java @@ -366,7 +366,7 @@ private final class AcquireTask extends AcquireListener { final long expireNanoTime = System.nanoTime() + acquireTimeoutNanos; ScheduledFuture timeoutFuture; - public AcquireTask(Promise promise) { + AcquireTask(Promise promise) { super(promise); // We need to create a new promise as we need to ensure the AcquireListener runs in the correct // EventLoop. diff --git a/transport/src/test/java/io/netty/channel/AbstractChannelTest.java b/transport/src/test/java/io/netty/channel/AbstractChannelTest.java index afbe27c04bed..fc5fb9b066d9 100644 --- a/transport/src/test/java/io/netty/channel/AbstractChannelTest.java +++ b/transport/src/test/java/io/netty/channel/AbstractChannelTest.java @@ -96,7 +96,7 @@ private class TestUnsafe extends AbstractUnsafe { public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { } } - public TestChannel() { + TestChannel() { super(null); } diff --git a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTailTest.java b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTailTest.java index 697db84af506..7eb624b6bc36 100644 --- a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTailTest.java +++ b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTailTest.java @@ -237,7 +237,7 @@ protected void onUnhandledInboundWritabilityChanged() { private static class MyChannelFactory implements ChannelFactory { private final MyChannel channel; - public MyChannelFactory(MyChannel channel) { + MyChannelFactory(MyChannel channel) { this.channel = channel; } @@ -365,7 +365,7 @@ public void connect(SocketAddress remoteAddress, SocketAddress localAddress, Cha private class MyChannelPipeline extends DefaultChannelPipeline { - public MyChannelPipeline(Channel channel) { + MyChannelPipeline(Channel channel) { super(channel); } From 948d4a9ec58aef092c84eb76e672fd06e81cc13c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 28 Jan 2019 19:45:38 +0100 Subject: [PATCH 359/417] Minimize memory footprint for AbstractChannelHandlerContext for handlers that execute in the EventExecutor. (#8786) Motivation: We cache the Runnable for some tasks to reduce GC pressure in 4 different fields. This gives overhead in terms of memory usage in all cases, even if we always execute in the EventExecutor (which is the case most of the times). Modifications: Move the 4 fields to another class and only have one reference to this in AbstractChannelHandlerContext. This gives a small overhead in the case of execution that is done outside of the EventExecutor but reduce memory footprint in the more likily execution case. Result: Less memory used per AbstractChannelHandlerContext in most cases. --- .../AbstractChannelHandlerContext.java | 89 ++++++++++--------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java index 6abf529afcc0..f24771a50eac 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java @@ -76,10 +76,7 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap // Lazily instantiated tasks used to trigger events to a handler with different executor. // There is no need to make this volatile as at worse it will just create a few more instances then needed. - private Runnable invokeChannelReadCompleteTask; - private Runnable invokeReadTask; - private Runnable invokeChannelWritableStateChangedTask; - private Runnable invokeFlushTask; + private Tasks invokeTasks; private volatile int handlerState = INIT; @@ -379,16 +376,11 @@ static void invokeChannelReadComplete(final AbstractChannelHandlerContext next) if (executor.inEventLoop()) { next.invokeChannelReadComplete(); } else { - Runnable task = next.invokeChannelReadCompleteTask; - if (task == null) { - next.invokeChannelReadCompleteTask = task = new Runnable() { - @Override - public void run() { - next.invokeChannelReadComplete(); - } - }; + Tasks tasks = next.invokeTasks; + if (tasks == null) { + next.invokeTasks = tasks = new Tasks(next); } - executor.execute(task); + executor.execute(tasks.invokeChannelReadCompleteTask); } } @@ -415,16 +407,11 @@ static void invokeChannelWritabilityChanged(final AbstractChannelHandlerContext if (executor.inEventLoop()) { next.invokeChannelWritabilityChanged(); } else { - Runnable task = next.invokeChannelWritableStateChangedTask; - if (task == null) { - next.invokeChannelWritableStateChangedTask = task = new Runnable() { - @Override - public void run() { - next.invokeChannelWritabilityChanged(); - } - }; + Tasks tasks = next.invokeTasks; + if (tasks == null) { + next.invokeTasks = tasks = new Tasks(next); } - executor.execute(task); + executor.execute(tasks.invokeChannelWritableStateChangedTask); } } @@ -672,16 +659,11 @@ public ChannelHandlerContext read() { if (executor.inEventLoop()) { next.invokeRead(); } else { - Runnable task = next.invokeReadTask; - if (task == null) { - next.invokeReadTask = task = new Runnable() { - @Override - public void run() { - next.invokeRead(); - } - }; + Tasks tasks = next.invokeTasks; + if (tasks == null) { + next.invokeTasks = tasks = new Tasks(next); } - executor.execute(task); + executor.execute(tasks.invokeReadTask); } return this; @@ -734,16 +716,11 @@ public ChannelHandlerContext flush() { if (executor.inEventLoop()) { next.invokeFlush(); } else { - Runnable task = next.invokeFlushTask; - if (task == null) { - next.invokeFlushTask = task = new Runnable() { - @Override - public void run() { - next.invokeFlush(); - } - }; + Tasks tasks = next.invokeTasks; + if (tasks == null) { + next.invokeTasks = tasks = new Tasks(next); } - safeExecute(executor, task, channel().voidPromise(), null); + safeExecute(executor, tasks.invokeFlushTask, channel().voidPromise(), null); } return this; @@ -1162,4 +1139,36 @@ public void write(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise ctx.invokeFlush(); } } + + private static final class Tasks { + private final AbstractChannelHandlerContext next; + private final Runnable invokeChannelReadCompleteTask = new Runnable() { + @Override + public void run() { + next.invokeChannelReadComplete(); + } + }; + private final Runnable invokeReadTask = new Runnable() { + @Override + public void run() { + next.invokeRead(); + } + }; + private final Runnable invokeChannelWritableStateChangedTask = new Runnable() { + @Override + public void run() { + next.invokeChannelWritabilityChanged(); + } + }; + private final Runnable invokeFlushTask = new Runnable() { + @Override + public void run() { + next.invokeFlush(); + } + }; + + Tasks(AbstractChannelHandlerContext next) { + this.next = next; + } + } } From a6e6a9151f09e48d92ed96df9b3f4437d78856dc Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 30 Jan 2019 09:45:54 +0100 Subject: [PATCH 360/417] Fix AppendableCharSequence.subSequence(...) where start == end. (#8798) Motivation: To conform to the CharSequence interface we need to return an empty CharSequence when start == end index and a subSequence is requested. Modifications: - Correctly handle the case where start == end - Add unit test Result: Fix https://github.com/netty/netty/issues/8796. --- .../io/netty/util/internal/AppendableCharSequence.java | 6 ++++++ .../util/internal/AppendableCharSequenceTest.java | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/common/src/main/java/io/netty/util/internal/AppendableCharSequence.java b/common/src/main/java/io/netty/util/internal/AppendableCharSequence.java index 408c32f38027..e8e6abf56613 100644 --- a/common/src/main/java/io/netty/util/internal/AppendableCharSequence.java +++ b/common/src/main/java/io/netty/util/internal/AppendableCharSequence.java @@ -63,6 +63,12 @@ public char charAtUnsafe(int index) { @Override public AppendableCharSequence subSequence(int start, int end) { + if (start == end) { + // If start and end index is the same we need to return an empty sequence to conform to the interface. + // As our expanding logic depends on the fact that we have a char[] with length > 0 we need to construct + // an instance for which this is true. + return new AppendableCharSequence(Math.min(16, chars.length)); + } return new AppendableCharSequence(Arrays.copyOfRange(chars, start, end)); } diff --git a/common/src/test/java/io/netty/util/internal/AppendableCharSequenceTest.java b/common/src/test/java/io/netty/util/internal/AppendableCharSequenceTest.java index 2d7bab4ec849..9d08c3ee5771 100644 --- a/common/src/test/java/io/netty/util/internal/AppendableCharSequenceTest.java +++ b/common/src/test/java/io/netty/util/internal/AppendableCharSequenceTest.java @@ -64,6 +64,16 @@ public void testSubSequence() { assertEquals("abcdefghij", master.subSequence(0, 10).toString()); } + @Test + public void testEmptySubSequence() { + AppendableCharSequence master = new AppendableCharSequence(26); + master.append("abcdefghijlkmonpqrstuvwxyz"); + AppendableCharSequence sub = master.subSequence(0, 0); + assertEquals(0, sub.length()); + sub.append('b'); + assertEquals('b', sub.charAt(0)); + } + private static void testSimpleAppend0(AppendableCharSequence seq) { String text = "testdata"; for (int i = 0; i < text.length(); i++) { From ff7484864b1785103cbc62845ff3a392c93822b7 Mon Sep 17 00:00:00 2001 From: Dmitriy Dumanskiy Date: Wed, 30 Jan 2019 22:17:00 +0200 Subject: [PATCH 361/417] Compare HttpMethod by reference (#8815) Motivation: In most cases, HttpMethod instance is built from the factory method and the same instance is taken for known Http Methods. So we can implement fast path for equals(). Modification: Replace == checks with HttpMethod.equals; Use this == o within HttpMethod.equals; Replaced known new HttpMethod with HttpMethod.valueOf; Result: Comparisons should be a bit faster in some cases. --- .../codec/http/HttpContentEncoder.java | 6 ++--- .../netty/handler/codec/http/HttpMethod.java | 6 +++++ .../handler/codec/http/HttpServerCodec.java | 3 ++- .../handler/codec/http/cors/CorsHandler.java | 2 +- ...bSocketServerProtocolHandshakeHandler.java | 2 +- .../netty/handler/codec/rtsp/RtspMethods.java | 22 +++++++++---------- .../file/HttpStaticFileServerHandler.java | 2 +- .../http/upload/HttpUploadServerHandler.java | 2 +- .../WebSocketServerHandler.java | 2 +- .../server/WebSocketIndexPageHandler.java | 2 +- .../autobahn/AutobahnServerHandler.java | 2 +- 11 files changed, 29 insertions(+), 22 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java index c486a67ed8a9..7be3b8b92e92 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java @@ -78,10 +78,10 @@ protected void decode(ChannelHandlerContext ctx, HttpRequest msg, List o acceptedEncoding = HttpContentDecoder.IDENTITY; } - HttpMethod meth = msg.method(); - if (meth == HttpMethod.HEAD) { + HttpMethod method = msg.method(); + if (HttpMethod.HEAD.equals(method)) { acceptedEncoding = ZERO_LENGTH_HEAD; - } else if (meth == HttpMethod.CONNECT) { + } else if (HttpMethod.CONNECT.equals(method)) { acceptedEncoding = ZERO_LENGTH_CONNECT; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMethod.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMethod.java index 3d975516d5fc..a634bd0016f3 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMethod.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMethod.java @@ -156,6 +156,9 @@ public int hashCode() { @Override public boolean equals(Object o) { + if (this == o) { + return true; + } if (!(o instanceof HttpMethod)) { return false; } @@ -171,6 +174,9 @@ public String toString() { @Override public int compareTo(HttpMethod o) { + if (o == this) { + return 0; + } return name().compareTo(o.name()); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java index e2551dcee146..6e36128fa23c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java @@ -117,7 +117,8 @@ private final class HttpServerResponseEncoder extends HttpResponseEncoder { @Override protected void sanitizeHeadersBeforeEncode(HttpResponse msg, boolean isAlwaysEmpty) { - if (!isAlwaysEmpty && method == HttpMethod.CONNECT && msg.status().codeClass() == HttpStatusClass.SUCCESS) { + if (!isAlwaysEmpty && HttpMethod.CONNECT.equals(method) + && msg.status().codeClass() == HttpStatusClass.SUCCESS) { // Stripping Transfer-Encoding: // See https://tools.ietf.org/html/rfc7230#section-3.3.1 msg.headers().remove(HttpHeaderNames.TRANSFER_ENCODING); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsHandler.java index 39f79a4cd1d8..f1e162f1acc2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsHandler.java @@ -191,7 +191,7 @@ private void setAllowCredentials(final HttpResponse response) { private static boolean isPreflightRequest(final HttpRequest request) { final HttpHeaders headers = request.headers(); - return request.method().equals(OPTIONS) && + return OPTIONS.equals(request.method()) && headers.contains(HttpHeaderNames.ORIGIN) && headers.contains(HttpHeaderNames.ACCESS_CONTROL_REQUEST_METHOD); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java index 26e01f1860fb..365a5859a6e4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java @@ -68,7 +68,7 @@ public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exce } try { - if (req.method() != GET) { + if (!GET.equals(req.method())) { sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN)); return; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspMethods.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspMethods.java index 3e6292801249..137f6fe8f5e8 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspMethods.java +++ b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspMethods.java @@ -38,62 +38,62 @@ public final class RtspMethods { * The DESCRIBE getMethod retrieves the description of a presentation or * media object identified by the request URL from a server. */ - public static final HttpMethod DESCRIBE = new HttpMethod("DESCRIBE"); + public static final HttpMethod DESCRIBE = HttpMethod.valueOf("DESCRIBE"); /** * The ANNOUNCE posts the description of a presentation or media object * identified by the request URL to a server, or updates the client-side * session description in real-time. */ - public static final HttpMethod ANNOUNCE = new HttpMethod("ANNOUNCE"); + public static final HttpMethod ANNOUNCE = HttpMethod.valueOf("ANNOUNCE"); /** * The SETUP request for a URI specifies the transport mechanism to be * used for the streamed media. */ - public static final HttpMethod SETUP = new HttpMethod("SETUP"); + public static final HttpMethod SETUP = HttpMethod.valueOf("SETUP"); /** * The PLAY getMethod tells the server to start sending data via the * mechanism specified in SETUP. */ - public static final HttpMethod PLAY = new HttpMethod("PLAY"); + public static final HttpMethod PLAY = HttpMethod.valueOf("PLAY"); /** * The PAUSE request causes the stream delivery to be interrupted * (halted) temporarily. */ - public static final HttpMethod PAUSE = new HttpMethod("PAUSE"); + public static final HttpMethod PAUSE = HttpMethod.valueOf("PAUSE"); /** * The TEARDOWN request stops the stream delivery for the given URI, * freeing the resources associated with it. */ - public static final HttpMethod TEARDOWN = new HttpMethod("TEARDOWN"); + public static final HttpMethod TEARDOWN = HttpMethod.valueOf("TEARDOWN"); /** * The GET_PARAMETER request retrieves the value of a parameter of a * presentation or stream specified in the URI. */ - public static final HttpMethod GET_PARAMETER = new HttpMethod("GET_PARAMETER"); + public static final HttpMethod GET_PARAMETER = HttpMethod.valueOf("GET_PARAMETER"); /** * The SET_PARAMETER requests to set the value of a parameter for a * presentation or stream specified by the URI. */ - public static final HttpMethod SET_PARAMETER = new HttpMethod("SET_PARAMETER"); + public static final HttpMethod SET_PARAMETER = HttpMethod.valueOf("SET_PARAMETER"); /** * The REDIRECT request informs the client that it must connect to another * server location. */ - public static final HttpMethod REDIRECT = new HttpMethod("REDIRECT"); + public static final HttpMethod REDIRECT = HttpMethod.valueOf("REDIRECT"); /** * The RECORD getMethod initiates recording a range of media data according to * the presentation description. */ - public static final HttpMethod RECORD = new HttpMethod("RECORD"); + public static final HttpMethod RECORD = HttpMethod.valueOf("RECORD"); private static final Map methodMap = new HashMap(); @@ -130,7 +130,7 @@ public static HttpMethod valueOf(String name) { if (result != null) { return result; } else { - return new HttpMethod(name); + return HttpMethod.valueOf(name); } } diff --git a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java index e2a801324dd1..8095fe419cc1 100644 --- a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java +++ b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java @@ -117,7 +117,7 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr return; } - if (request.method() != GET) { + if (!GET.equals(request.method())) { sendError(ctx, METHOD_NOT_ALLOWED); return; } diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java index 43f2d1399c0d..8c9cd2948b0e 100644 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java +++ b/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java @@ -145,7 +145,7 @@ public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Excep responseContent.append("\r\n\r\n"); // if GET Method: should not try to create a HttpPostRequestDecoder - if (request.method().equals(HttpMethod.GET)) { + if (HttpMethod.GET.equals(request.method())) { // GET Method: should not try to create a HttpPostRequestDecoder // So stop here responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n"); diff --git a/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServerHandler.java b/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServerHandler.java index 78f61ba14f45..a31d4f308e89 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServerHandler.java +++ b/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServerHandler.java @@ -71,7 +71,7 @@ private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) { } // Allow only GET methods. - if (req.method() != GET) { + if (!GET.equals(req.method())) { sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN)); return; } diff --git a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketIndexPageHandler.java b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketIndexPageHandler.java index 7d543ce39965..124415d12afb 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketIndexPageHandler.java +++ b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketIndexPageHandler.java @@ -58,7 +58,7 @@ protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) thro } // Allow only GET methods. - if (req.method() != GET) { + if (!GET.equals(req.method())) { sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN)); return; } diff --git a/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/AutobahnServerHandler.java b/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/AutobahnServerHandler.java index 75f40621e4bd..933c135887b0 100644 --- a/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/AutobahnServerHandler.java +++ b/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/AutobahnServerHandler.java @@ -78,7 +78,7 @@ private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) } // Allow only GET methods. - if (req.method() != GET) { + if (!GET.equals(req.method())) { sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN)); return; } From fe4a59011addc5a556ec882b5795db241f6c77c9 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 31 Jan 2019 08:56:01 +0100 Subject: [PATCH 362/417] Do not schedule notify task if there are no listeners attached to the promise. (#8797) Motivation: If there are no listeners attached to the promise when full-filling it we do not need to schedule a task to notify. Modifications: - Don't schedule a task if there is nothing to notify. - Add unit tests. Result: Fixes https://github.com/netty/netty/issues/8795. --- .../netty/util/concurrent/DefaultPromise.java | 30 ++++++++-------- .../util/concurrent/DefaultPromiseTest.java | 36 +++++++++++++++++++ 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java b/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java index a910e408c11c..99f946a831e6 100644 --- a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java +++ b/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java @@ -91,7 +91,6 @@ protected DefaultPromise() { @Override public Promise setSuccess(V result) { if (setSuccess0(result)) { - notifyListeners(); return this; } throw new IllegalStateException("complete already: " + this); @@ -99,17 +98,12 @@ public Promise setSuccess(V result) { @Override public boolean trySuccess(V result) { - if (setSuccess0(result)) { - notifyListeners(); - return true; - } - return false; + return setSuccess0(result); } @Override public Promise setFailure(Throwable cause) { if (setFailure0(cause)) { - notifyListeners(); return this; } throw new IllegalStateException("complete already: " + this, cause); @@ -117,11 +111,7 @@ public Promise setFailure(Throwable cause) { @Override public boolean tryFailure(Throwable cause) { - if (setFailure0(cause)) { - notifyListeners(); - return true; - } - return false; + return setFailure0(cause); } @Override @@ -315,8 +305,9 @@ public V getNow() { @Override public boolean cancel(boolean mayInterruptIfRunning) { if (RESULT_UPDATER.compareAndSet(this, null, CANCELLATION_CAUSE_HOLDER)) { - checkNotifyWaiters(); - notifyListeners(); + if (checkNotifyWaiters()) { + notifyListeners(); + } return true; } return false; @@ -545,16 +536,23 @@ private boolean setFailure0(Throwable cause) { private boolean setValue0(Object objResult) { if (RESULT_UPDATER.compareAndSet(this, null, objResult) || RESULT_UPDATER.compareAndSet(this, UNCANCELLABLE, objResult)) { - checkNotifyWaiters(); + if (checkNotifyWaiters()) { + notifyListeners(); + } return true; } return false; } - private synchronized void checkNotifyWaiters() { + /** + * Check if there are any waiters and if so notify these. + * @return {@code true} if there are any listeners attached to the promise, {@code false} otherwise. + */ + private synchronized boolean checkNotifyWaiters() { if (waiters > 0) { notifyAll(); } + return listeners != null; } private void incWaiters() { diff --git a/common/src/test/java/io/netty/util/concurrent/DefaultPromiseTest.java b/common/src/test/java/io/netty/util/concurrent/DefaultPromiseTest.java index fd9a3e95d695..b5e5907293a1 100644 --- a/common/src/test/java/io/netty/util/concurrent/DefaultPromiseTest.java +++ b/common/src/test/java/io/netty/util/concurrent/DefaultPromiseTest.java @@ -21,6 +21,7 @@ import io.netty.util.internal.logging.InternalLoggerFactory; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.Mockito; import java.util.HashMap; import java.util.Map; @@ -63,6 +64,41 @@ private static int stackOverflowTestDepth() { return max(stackOverflowDepth << 1, stackOverflowDepth); } + @Test + public void testCancelDoesNotScheduleWhenNoListeners() { + EventExecutor executor = Mockito.mock(EventExecutor.class); + Mockito.when(executor.inEventLoop()).thenReturn(false); + + Promise promise = new DefaultPromise(executor); + promise.cancel(false); + Mockito.verify(executor, Mockito.never()).execute(Mockito.any(Runnable.class)); + assertTrue(promise.isCancelled()); + } + + @Test + public void testSuccessDoesNotScheduleWhenNoListeners() { + EventExecutor executor = Mockito.mock(EventExecutor.class); + Mockito.when(executor.inEventLoop()).thenReturn(false); + + Object value = new Object(); + Promise promise = new DefaultPromise(executor); + promise.setSuccess(value); + Mockito.verify(executor, Mockito.never()).execute(Mockito.any(Runnable.class)); + assertSame(value, promise.getNow()); + } + + @Test + public void testFailureDoesNotScheduleWhenNoListeners() { + EventExecutor executor = Mockito.mock(EventExecutor.class); + Mockito.when(executor.inEventLoop()).thenReturn(false); + + Exception cause = new Exception(); + Promise promise = new DefaultPromise(executor); + promise.setFailure(cause); + Mockito.verify(executor, Mockito.never()).execute(Mockito.any(Runnable.class)); + assertSame(cause, promise.cause()); + } + @Test(expected = CancellationException.class) public void testCancellationExceptionIsThrownWhenBlockingGet() throws InterruptedException, ExecutionException { final Promise promise = new DefaultPromise(ImmediateEventExecutor.INSTANCE); From a33200ca38990b88315a48637e5ac5da398b100d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Thu, 31 Jan 2019 16:06:59 +0800 Subject: [PATCH 363/417] use checkPositive/checkPositiveOrZero (#8803) Motivation: We have a utility method to check for > 0 and >0 arguments. We should use it. Modification: use checkPositive/checkPositiveOrZero instead of if statement. Result: Re-use utility method. --- .../java/io/netty/buffer/AbstractByteBuf.java | 19 +++++----------- .../buffer/AbstractByteBufAllocator.java | 10 ++++----- .../java/io/netty/buffer/ByteBufUtil.java | 9 +++----- .../java/io/netty/buffer/EmptyByteBuf.java | 18 +++++---------- .../main/java/io/netty/buffer/PoolArena.java | 5 ++--- .../java/io/netty/buffer/PoolThreadCache.java | 7 +++--- .../netty/buffer/PooledByteBufAllocator.java | 15 +++++-------- .../netty/buffer/UnpooledDirectByteBuf.java | 10 ++++----- .../buffer/UnpooledUnsafeDirectByteBuf.java | 10 ++++----- .../handler/codec/ByteToMessageDecoder.java | 6 ++--- .../codec/DelimiterBasedFrameDecoder.java | 8 +++---- .../codec/FixedLengthFrameDecoder.java | 7 +++--- .../codec/LengthFieldBasedFrameDecoder.java | 21 +++++------------- .../handler/codec/LengthFieldPrepender.java | 7 +++--- .../handler/codec/MessageAggregator.java | 5 ++--- .../http2/Http2ServerInitializer.java | 6 ++--- .../epoll/AbstractEpollStreamChannel.java | 13 ++++------- .../epoll/EpollServerChannelConfig.java | 9 +++----- .../kqueue/KQueueServerChannelConfig.java | 5 ++--- .../io/netty/channel/unix/FileDescriptor.java | 5 ++--- .../sctp/DefaultSctpServerChannelConfig.java | 6 ++--- .../channel/AdaptiveRecvByteBufAllocator.java | 5 ++--- .../channel/ChannelFlushPromiseNotifier.java | 10 ++++----- .../io/netty/channel/ChannelMetadata.java | 7 +++--- .../netty/channel/DefaultChannelConfig.java | 22 +++++-------------- .../io/netty/channel/DefaultFileRegion.java | 18 +++++---------- .../DefaultMaxBytesRecvByteBufAllocator.java | 20 +++++------------ ...efaultMaxMessagesRecvByteBufAllocator.java | 6 ++--- .../channel/DefaultMessageSizeEstimator.java | 6 ++--- .../channel/FixedRecvByteBufAllocator.java | 7 +++--- .../netty/channel/WriteBufferWaterMark.java | 6 ++--- .../DefaultServerSocketChannelConfig.java | 5 ++--- 32 files changed, 114 insertions(+), 199 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index 6864cee4da44..aa5e5e3af128 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -38,6 +38,7 @@ import java.nio.charset.Charset; import static io.netty.util.internal.MathUtil.isOutOfBounds; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; /** * A skeletal implementation of a buffer. @@ -73,9 +74,7 @@ public abstract class AbstractByteBuf extends ByteBuf { private int maxCapacity; protected AbstractByteBuf(int maxCapacity) { - if (maxCapacity < 0) { - throw new IllegalArgumentException("maxCapacity: " + maxCapacity + " (expected: >= 0)"); - } + checkPositiveOrZero(maxCapacity, "maxCapacity"); this.maxCapacity = maxCapacity; } @@ -271,10 +270,7 @@ protected final void adjustMarkers(int decrement) { @Override public ByteBuf ensureWritable(int minWritableBytes) { - if (minWritableBytes < 0) { - throw new IllegalArgumentException(String.format( - "minWritableBytes: %d (expected: >= 0)", minWritableBytes)); - } + checkPositiveOrZero(minWritableBytes, "minWritableBytes"); ensureWritable0(minWritableBytes); return this; } @@ -302,10 +298,7 @@ final void ensureWritable0(int minWritableBytes) { @Override public int ensureWritable(int minWritableBytes, boolean force) { ensureAccessible(); - if (minWritableBytes < 0) { - throw new IllegalArgumentException(String.format( - "minWritableBytes: %d (expected: >= 0)", minWritableBytes)); - } + checkPositiveOrZero(minWritableBytes, "minWritableBytes"); if (minWritableBytes <= writableBytes()) { return 0; @@ -1414,9 +1407,7 @@ protected final void checkDstIndex(int index, int length, int dstIndex, int dstC * than the specified value. */ protected final void checkReadableBytes(int minimumReadableBytes) { - if (minimumReadableBytes < 0) { - throw new IllegalArgumentException("minimumReadableBytes: " + minimumReadableBytes + " (expected: >= 0)"); - } + checkPositiveOrZero(minimumReadableBytes, "minimumReadableBytes"); checkReadableBytes0(minimumReadableBytes); } diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java index 40525144e3f3..920b3fb46d04 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java @@ -16,6 +16,8 @@ package io.netty.buffer; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.util.ResourceLeakDetector; import io.netty.util.ResourceLeakTracker; import io.netty.util.internal.PlatformDependent; @@ -222,9 +224,7 @@ public CompositeByteBuf compositeDirectBuffer(int maxNumComponents) { } private static void validate(int initialCapacity, int maxCapacity) { - if (initialCapacity < 0) { - throw new IllegalArgumentException("initialCapacity: " + initialCapacity + " (expected: 0+)"); - } + checkPositiveOrZero(initialCapacity, "initialCapacity"); if (initialCapacity > maxCapacity) { throw new IllegalArgumentException(String.format( "initialCapacity: %d (expected: not greater than maxCapacity(%d)", @@ -249,9 +249,7 @@ public String toString() { @Override public int calculateNewCapacity(int minNewCapacity, int maxCapacity) { - if (minNewCapacity < 0) { - throw new IllegalArgumentException("minNewCapacity: " + minNewCapacity + " (expected: 0+)"); - } + checkPositiveOrZero(minNewCapacity, "minNewCapacity"); if (minNewCapacity > maxCapacity) { throw new IllegalArgumentException(String.format( "minNewCapacity: %d (expected: not greater than maxCapacity(%d)", diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index d2b2ec334f98..54a9f0c9fa02 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -43,6 +43,7 @@ import static io.netty.util.internal.MathUtil.isOutOfBounds; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; import static io.netty.util.internal.StringUtil.NEWLINE; import static io.netty.util.internal.StringUtil.isSurrogate; @@ -1000,9 +1001,7 @@ private static final class HexUtil { } private static String hexDump(ByteBuf buffer, int fromIndex, int length) { - if (length < 0) { - throw new IllegalArgumentException("length: " + length); - } + checkPositiveOrZero(length, "length"); if (length == 0) { return ""; } @@ -1022,9 +1021,7 @@ private static String hexDump(ByteBuf buffer, int fromIndex, int length) { } private static String hexDump(byte[] array, int fromIndex, int length) { - if (length < 0) { - throw new IllegalArgumentException("length: " + length); - } + checkPositiveOrZero(length, "length"); if (length == 0) { return ""; } diff --git a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java index b954318a7f81..cbc8a1acf8c3 100644 --- a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java @@ -16,6 +16,8 @@ package io.netty.buffer; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.util.ByteProcessor; import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.PlatformDependent; @@ -223,9 +225,7 @@ public ByteBuf discardSomeReadBytes() { @Override public ByteBuf ensureWritable(int minWritableBytes) { - if (minWritableBytes < 0) { - throw new IllegalArgumentException("minWritableBytes: " + minWritableBytes + " (expected: >= 0)"); - } + checkPositiveOrZero(minWritableBytes, "minWritableBytes"); if (minWritableBytes != 0) { throw new IndexOutOfBoundsException(); } @@ -234,9 +234,7 @@ public ByteBuf ensureWritable(int minWritableBytes) { @Override public int ensureWritable(int minWritableBytes, boolean force) { - if (minWritableBytes < 0) { - throw new IllegalArgumentException("minWritableBytes: " + minWritableBytes + " (expected: >= 0)"); - } + checkPositiveOrZero(minWritableBytes, "minWritableBytes"); if (minWritableBytes == 0) { return 0; @@ -1048,9 +1046,7 @@ private ByteBuf checkIndex(int index) { } private ByteBuf checkIndex(int index, int length) { - if (length < 0) { - throw new IllegalArgumentException("length: " + length); - } + checkPositiveOrZero(length, "length"); if (index != 0 || length != 0) { throw new IndexOutOfBoundsException(); } @@ -1058,9 +1054,7 @@ private ByteBuf checkIndex(int index, int length) { } private ByteBuf checkLength(int length) { - if (length < 0) { - throw new IllegalArgumentException("length: " + length + " (expected: >= 0)"); - } + checkPositiveOrZero(length, "length"); if (length != 0) { throw new IndexOutOfBoundsException(); } diff --git a/buffer/src/main/java/io/netty/buffer/PoolArena.java b/buffer/src/main/java/io/netty/buffer/PoolArena.java index 3bc948be412d..5f1ec79ffde5 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolArena.java +++ b/buffer/src/main/java/io/netty/buffer/PoolArena.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; import static java.lang.Math.max; abstract class PoolArena implements PoolArenaMetric { @@ -330,9 +331,7 @@ PoolSubpage findSubpagePoolHead(int elemSize) { } int normalizeCapacity(int reqCapacity) { - if (reqCapacity < 0) { - throw new IllegalArgumentException("capacity: " + reqCapacity + " (expected: 0+)"); - } + checkPositiveOrZero(reqCapacity, "reqCapacity"); if (reqCapacity >= chunkSize) { return directMemoryCacheAlignment == 0 ? reqCapacity : alignCapacity(reqCapacity); diff --git a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java index de2a9be0604c..01a69d570064 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java +++ b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java @@ -17,6 +17,8 @@ package io.netty.buffer; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.buffer.PoolArena.SizeClass; import io.netty.util.Recycler; import io.netty.util.Recycler.Handle; @@ -65,10 +67,7 @@ final class PoolThreadCache { PoolThreadCache(PoolArena heapArena, PoolArena directArena, int tinyCacheSize, int smallCacheSize, int normalCacheSize, int maxCachedBufferCapacity, int freeSweepAllocationThreshold) { - if (maxCachedBufferCapacity < 0) { - throw new IllegalArgumentException("maxCachedBufferCapacity: " - + maxCachedBufferCapacity + " (expected: >= 0)"); - } + checkPositiveOrZero(maxCachedBufferCapacity, "maxCachedBufferCapacity"); this.freeSweepAllocationThreshold = freeSweepAllocationThreshold; this.heapArena = heapArena; this.directArena = directArena; diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java index de6eee1d54dc..bcfc9ceb1a13 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java @@ -16,6 +16,8 @@ package io.netty.buffer; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.util.NettyRuntime; import io.netty.util.concurrent.FastThreadLocal; import io.netty.util.concurrent.FastThreadLocalThread; @@ -215,17 +217,10 @@ public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectA this.normalCacheSize = normalCacheSize; chunkSize = validateAndCalculateChunkSize(pageSize, maxOrder); - if (nHeapArena < 0) { - throw new IllegalArgumentException("nHeapArena: " + nHeapArena + " (expected: >= 0)"); - } - if (nDirectArena < 0) { - throw new IllegalArgumentException("nDirectArea: " + nDirectArena + " (expected: >= 0)"); - } + checkPositiveOrZero(nHeapArena, "nHeapArena"); + checkPositiveOrZero(nDirectArena, "nDirectArena"); - if (directMemoryCacheAlignment < 0) { - throw new IllegalArgumentException("directMemoryCacheAlignment: " - + directMemoryCacheAlignment + " (expected: >= 0)"); - } + checkPositiveOrZero(directMemoryCacheAlignment, "directMemoryCacheAlignment"); if (directMemoryCacheAlignment > 0 && !isDirectMemoryCacheAlignmentSupported()) { throw new IllegalArgumentException("directMemoryCacheAlignment is not supported"); } diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java index 60167cf341f8..f64cd2587fe6 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.util.internal.PlatformDependent; import java.io.IOException; @@ -52,12 +54,8 @@ public UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int ma if (alloc == null) { throw new NullPointerException("alloc"); } - if (initialCapacity < 0) { - throw new IllegalArgumentException("initialCapacity: " + initialCapacity); - } - if (maxCapacity < 0) { - throw new IllegalArgumentException("maxCapacity: " + maxCapacity); - } + checkPositiveOrZero(initialCapacity, "initialCapacity"); + checkPositiveOrZero(maxCapacity, "maxCapacity"); if (initialCapacity > maxCapacity) { throw new IllegalArgumentException(String.format( "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity)); diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java index 9d425e322e39..3bccd08a08b3 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.util.internal.PlatformDependent; import java.io.IOException; @@ -53,12 +55,8 @@ public UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, if (alloc == null) { throw new NullPointerException("alloc"); } - if (initialCapacity < 0) { - throw new IllegalArgumentException("initialCapacity: " + initialCapacity); - } - if (maxCapacity < 0) { - throw new IllegalArgumentException("maxCapacity: " + maxCapacity); - } + checkPositiveOrZero(initialCapacity, "initialCapacity"); + checkPositiveOrZero(maxCapacity, "maxCapacity"); if (initialCapacity > maxCapacity) { throw new IllegalArgumentException(String.format( "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity)); diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java index ddf3d9b14fae..bed1efc211cf 100644 --- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec; +import static io.netty.util.internal.ObjectUtil.checkPositive; + import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.CompositeByteBuf; @@ -202,9 +204,7 @@ public void setCumulator(Cumulator cumulator) { * The default is {@code 16}. */ public void setDiscardAfterReads(int discardAfterReads) { - if (discardAfterReads <= 0) { - throw new IllegalArgumentException("discardAfterReads must be > 0"); - } + checkPositive(discardAfterReads, "discardAfterReads"); this.discardAfterReads = discardAfterReads; } diff --git a/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java index 27e8c201576e..2fb6fec729b5 100644 --- a/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec; +import static io.netty.util.internal.ObjectUtil.checkPositive; + import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; @@ -346,10 +348,6 @@ private static void validateDelimiter(ByteBuf delimiter) { } private static void validateMaxFrameLength(int maxFrameLength) { - if (maxFrameLength <= 0) { - throw new IllegalArgumentException( - "maxFrameLength must be a positive integer: " + - maxFrameLength); - } + checkPositive(maxFrameLength, "maxFrameLength"); } } diff --git a/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java index 5b4bb7187c50..9475e5b65118 100644 --- a/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec; +import static io.netty.util.internal.ObjectUtil.checkPositive; + import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; @@ -46,10 +48,7 @@ public class FixedLengthFrameDecoder extends ByteToMessageDecoder { * @param frameLength the length of the frame */ public FixedLengthFrameDecoder(int frameLength) { - if (frameLength <= 0) { - throw new IllegalArgumentException( - "frameLength must be a positive integer: " + frameLength); - } + checkPositive(frameLength, "frameLength"); this.frameLength = frameLength; } diff --git a/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java index 4d94bdf881a5..b2a5cac49bf7 100644 --- a/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java @@ -15,6 +15,9 @@ */ package io.netty.handler.codec; +import static io.netty.util.internal.ObjectUtil.checkPositive; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import java.nio.ByteOrder; import java.util.List; @@ -302,23 +305,11 @@ public LengthFieldBasedFrameDecoder( throw new NullPointerException("byteOrder"); } - if (maxFrameLength <= 0) { - throw new IllegalArgumentException( - "maxFrameLength must be a positive integer: " + - maxFrameLength); - } + checkPositive(maxFrameLength, "maxFrameLength"); - if (lengthFieldOffset < 0) { - throw new IllegalArgumentException( - "lengthFieldOffset must be a non-negative integer: " + - lengthFieldOffset); - } + checkPositiveOrZero(lengthFieldOffset, "lengthFieldOffset"); - if (initialBytesToStrip < 0) { - throw new IllegalArgumentException( - "initialBytesToStrip must be a non-negative integer: " + - initialBytesToStrip); - } + checkPositiveOrZero(initialBytesToStrip, "initialBytesToStrip"); if (lengthFieldOffset > maxFrameLength - lengthFieldLength) { throw new IllegalArgumentException( diff --git a/codec/src/main/java/io/netty/handler/codec/LengthFieldPrepender.java b/codec/src/main/java/io/netty/handler/codec/LengthFieldPrepender.java index 4076e07deed9..b5c787f531fe 100644 --- a/codec/src/main/java/io/netty/handler/codec/LengthFieldPrepender.java +++ b/codec/src/main/java/io/netty/handler/codec/LengthFieldPrepender.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; @@ -163,10 +165,7 @@ protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) length += lengthFieldLength; } - if (length < 0) { - throw new IllegalArgumentException( - "Adjusted frame length (" + length + ") is less than zero"); - } + checkPositiveOrZero(length, "length"); switch (lengthFieldLength) { case 1: diff --git a/codec/src/main/java/io/netty/handler/codec/MessageAggregator.java b/codec/src/main/java/io/netty/handler/codec/MessageAggregator.java index 2cdb880c9932..880756224b6b 100644 --- a/codec/src/main/java/io/netty/handler/codec/MessageAggregator.java +++ b/codec/src/main/java/io/netty/handler/codec/MessageAggregator.java @@ -28,6 +28,7 @@ import java.util.List; import static io.netty.buffer.Unpooled.EMPTY_BUFFER; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; /** * An abstract {@link ChannelHandler} that aggregates a series of message objects into a single aggregated message. @@ -81,9 +82,7 @@ protected MessageAggregator(int maxContentLength, Class inboundMess } private static void validateMaxContentLength(int maxContentLength) { - if (maxContentLength < 0) { - throw new IllegalArgumentException("maxContentLength: " + maxContentLength + " (expected: >= 0)"); - } + checkPositiveOrZero(maxContentLength, "maxContentLength"); } @Override diff --git a/testsuite-http2/src/main/java/io/netty/testsuite/http2/Http2ServerInitializer.java b/testsuite-http2/src/main/java/io/netty/testsuite/http2/Http2ServerInitializer.java index 7b67fc9438cb..0af251cddd73 100644 --- a/testsuite-http2/src/main/java/io/netty/testsuite/http2/Http2ServerInitializer.java +++ b/testsuite-http2/src/main/java/io/netty/testsuite/http2/Http2ServerInitializer.java @@ -16,6 +16,8 @@ package io.netty.testsuite.http2; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; @@ -59,9 +61,7 @@ public UpgradeCodec newUpgradeCodec(CharSequence protocol) { } Http2ServerInitializer(int maxHttpContentLength) { - if (maxHttpContentLength < 0) { - throw new IllegalArgumentException("maxHttpContentLength (expected >= 0): " + maxHttpContentLength); - } + checkPositiveOrZero(maxHttpContentLength, "maxHttpContentLength"); this.maxHttpContentLength = maxHttpContentLength; } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java index 70208d2672e3..8d993b48e74b 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java @@ -54,6 +54,7 @@ import static io.netty.channel.internal.ChannelUtils.WRITE_STATUS_SNDBUF_FULL; import static io.netty.channel.unix.FileDescriptor.pipe; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel implements DuplexChannel { private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16); @@ -163,9 +164,7 @@ public final ChannelFuture spliceTo(final AbstractEpollStreamChannel ch, final i if (ch.eventLoop() != eventLoop()) { throw new IllegalArgumentException("EventLoops are not the same."); } - if (len < 0) { - throw new IllegalArgumentException("len: " + len + " (expected: >= 0)"); - } + checkPositiveOrZero(len, "len"); if (ch.config().getEpollMode() != EpollMode.LEVEL_TRIGGERED || config().getEpollMode() != EpollMode.LEVEL_TRIGGERED) { throw new IllegalStateException("spliceTo() supported only when using " + EpollMode.LEVEL_TRIGGERED); @@ -214,12 +213,8 @@ public final ChannelFuture spliceTo(final FileDescriptor ch, final int offset, f */ public final ChannelFuture spliceTo(final FileDescriptor ch, final int offset, final int len, final ChannelPromise promise) { - if (len < 0) { - throw new IllegalArgumentException("len: " + len + " (expected: >= 0)"); - } - if (offset < 0) { - throw new IllegalArgumentException("offset must be >= 0 but was " + offset); - } + checkPositiveOrZero(len, "len"); + checkPositiveOrZero(offset, "offser"); if (config().getEpollMode() != EpollMode.LEVEL_TRIGGERED) { throw new IllegalStateException("spliceTo() supported only when using " + EpollMode.LEVEL_TRIGGERED); } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java index 514b6c34cc4e..aa3409cf84c0 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java @@ -30,6 +30,7 @@ import static io.netty.channel.ChannelOption.SO_BACKLOG; import static io.netty.channel.ChannelOption.SO_RCVBUF; import static io.netty.channel.ChannelOption.SO_REUSEADDR; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; public class EpollServerChannelConfig extends EpollChannelConfig implements ServerSocketChannelConfig { private volatile int backlog = NetUtil.SOMAXCONN; @@ -120,9 +121,7 @@ public int getBacklog() { } public EpollServerChannelConfig setBacklog(int backlog) { - if (backlog < 0) { - throw new IllegalArgumentException("backlog: " + backlog); - } + checkPositiveOrZero(backlog, "backlog"); this.backlog = backlog; return this; } @@ -146,9 +145,7 @@ public int getTcpFastopen() { * @see RFC 7413 TCP FastOpen */ public EpollServerChannelConfig setTcpFastopen(int pendingFastOpenRequestsThreshold) { - if (this.pendingFastOpenRequestsThreshold < 0) { - throw new IllegalArgumentException("pendingFastOpenRequestsThreshold: " + pendingFastOpenRequestsThreshold); - } + checkPositiveOrZero(this.pendingFastOpenRequestsThreshold, "pendingFastOpenRequestsThreshold"); this.pendingFastOpenRequestsThreshold = pendingFastOpenRequestsThreshold; return this; } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerChannelConfig.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerChannelConfig.java index 09291f58dbf9..cf103f9a3ae5 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerChannelConfig.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueServerChannelConfig.java @@ -31,6 +31,7 @@ import static io.netty.channel.ChannelOption.SO_BACKLOG; import static io.netty.channel.ChannelOption.SO_RCVBUF; import static io.netty.channel.ChannelOption.SO_REUSEADDR; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; @UnstableApi public class KQueueServerChannelConfig extends KQueueChannelConfig implements ServerSocketChannelConfig { @@ -116,9 +117,7 @@ public int getBacklog() { } public KQueueServerChannelConfig setBacklog(int backlog) { - if (backlog < 0) { - throw new IllegalArgumentException("backlog: " + backlog); - } + checkPositiveOrZero(backlog, "backlog"); this.backlog = backlog; return this; } diff --git a/transport-native-unix-common/src/main/java/io/netty/channel/unix/FileDescriptor.java b/transport-native-unix-common/src/main/java/io/netty/channel/unix/FileDescriptor.java index cf26a42054c4..415d9b79eedc 100644 --- a/transport-native-unix-common/src/main/java/io/netty/channel/unix/FileDescriptor.java +++ b/transport-native-unix-common/src/main/java/io/netty/channel/unix/FileDescriptor.java @@ -27,6 +27,7 @@ import static io.netty.channel.unix.Errors.newIOException; import static io.netty.channel.unix.Limits.IOV_MAX; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; import static java.lang.Math.min; /** @@ -82,9 +83,7 @@ public class FileDescriptor { final int fd; public FileDescriptor(int fd) { - if (fd < 0) { - throw new IllegalArgumentException("fd must be >= 0"); - } + checkPositiveOrZero(fd, "fd"); this.fd = fd; } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java index 4980eb88f6f6..aded9431847c 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java @@ -15,6 +15,8 @@ */ package io.netty.channel.sctp; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import com.sun.nio.sctp.SctpServerChannel; import com.sun.nio.sctp.SctpStandardSocketOptions; import io.netty.buffer.ByteBufAllocator; @@ -152,9 +154,7 @@ public int getBacklog() { @Override public SctpServerChannelConfig setBacklog(int backlog) { - if (backlog < 0) { - throw new IllegalArgumentException("backlog: " + backlog); - } + checkPositiveOrZero(backlog, "backlog"); this.backlog = backlog; return this; } diff --git a/transport/src/main/java/io/netty/channel/AdaptiveRecvByteBufAllocator.java b/transport/src/main/java/io/netty/channel/AdaptiveRecvByteBufAllocator.java index e7227db82e34..a4ab9fe5faae 100644 --- a/transport/src/main/java/io/netty/channel/AdaptiveRecvByteBufAllocator.java +++ b/transport/src/main/java/io/netty/channel/AdaptiveRecvByteBufAllocator.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import static io.netty.util.internal.ObjectUtil.checkPositive; import static java.lang.Math.max; import static java.lang.Math.min; @@ -163,9 +164,7 @@ public AdaptiveRecvByteBufAllocator() { * @param maximum the inclusive upper bound of the expected buffer size */ public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) { - if (minimum <= 0) { - throw new IllegalArgumentException("minimum: " + minimum); - } + checkPositive(minimum, "minimum"); if (initial < minimum) { throw new IllegalArgumentException("initial: " + initial); } diff --git a/transport/src/main/java/io/netty/channel/ChannelFlushPromiseNotifier.java b/transport/src/main/java/io/netty/channel/ChannelFlushPromiseNotifier.java index 26594a38df5a..ff0b9c031b87 100644 --- a/transport/src/main/java/io/netty/channel/ChannelFlushPromiseNotifier.java +++ b/transport/src/main/java/io/netty/channel/ChannelFlushPromiseNotifier.java @@ -15,6 +15,8 @@ */ package io.netty.channel; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import java.util.ArrayDeque; import java.util.Queue; @@ -64,9 +66,7 @@ public ChannelFlushPromiseNotifier add(ChannelPromise promise, long pendingDataS if (promise == null) { throw new NullPointerException("promise"); } - if (pendingDataSize < 0) { - throw new IllegalArgumentException("pendingDataSize must be >= 0 but was " + pendingDataSize); - } + checkPositiveOrZero(pendingDataSize, "pendingDataSize"); long checkpoint = writeCounter + pendingDataSize; if (promise instanceof FlushCheckpoint) { FlushCheckpoint cp = (FlushCheckpoint) promise; @@ -81,9 +81,7 @@ public ChannelFlushPromiseNotifier add(ChannelPromise promise, long pendingDataS * Increase the current write counter by the given delta */ public ChannelFlushPromiseNotifier increaseWriteCounter(long delta) { - if (delta < 0) { - throw new IllegalArgumentException("delta must be >= 0 but was " + delta); - } + checkPositiveOrZero(delta, "delta"); writeCounter += delta; return this; } diff --git a/transport/src/main/java/io/netty/channel/ChannelMetadata.java b/transport/src/main/java/io/netty/channel/ChannelMetadata.java index c77f53091668..e3b116b9883a 100644 --- a/transport/src/main/java/io/netty/channel/ChannelMetadata.java +++ b/transport/src/main/java/io/netty/channel/ChannelMetadata.java @@ -15,6 +15,8 @@ */ package io.netty.channel; +import static io.netty.util.internal.ObjectUtil.checkPositive; + import java.net.SocketAddress; /** @@ -46,10 +48,7 @@ public ChannelMetadata(boolean hasDisconnect) { * set for {@link MaxMessagesRecvByteBufAllocator#maxMessagesPerRead()}. Must be {@code > 0}. */ public ChannelMetadata(boolean hasDisconnect, int defaultMaxMessagesPerRead) { - if (defaultMaxMessagesPerRead <= 0) { - throw new IllegalArgumentException("defaultMaxMessagesPerRead: " + defaultMaxMessagesPerRead + - " (expected > 0)"); - } + checkPositive(defaultMaxMessagesPerRead, "defaultMaxMessagesPerRead"); this.hasDisconnect = hasDisconnect; this.defaultMaxMessagesPerRead = defaultMaxMessagesPerRead; } diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelConfig.java b/transport/src/main/java/io/netty/channel/DefaultChannelConfig.java index 4118708637ce..405678a26987 100644 --- a/transport/src/main/java/io/netty/channel/DefaultChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelConfig.java @@ -36,6 +36,8 @@ import static io.netty.channel.ChannelOption.WRITE_BUFFER_WATER_MARK; import static io.netty.channel.ChannelOption.WRITE_SPIN_COUNT; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.ObjectUtil.checkPositive; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; /** * The default {@link ChannelConfig} implementation. @@ -209,10 +211,7 @@ public int getConnectTimeoutMillis() { @Override public ChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) { - if (connectTimeoutMillis < 0) { - throw new IllegalArgumentException(String.format( - "connectTimeoutMillis: %d (expected: >= 0)", connectTimeoutMillis)); - } + checkPositiveOrZero(connectTimeoutMillis, "connectTimeoutMillis"); this.connectTimeoutMillis = connectTimeoutMillis; return this; } @@ -261,10 +260,7 @@ public int getWriteSpinCount() { @Override public ChannelConfig setWriteSpinCount(int writeSpinCount) { - if (writeSpinCount <= 0) { - throw new IllegalArgumentException( - "writeSpinCount must be a positive integer."); - } + checkPositive(writeSpinCount, "writeSpinCount"); // Integer.MAX_VALUE is used as a special value in the channel implementations to indicate the channel cannot // accept any more data, and results in the writeOp being set on the selector (or execute a runnable which tries // to flush later because the writeSpinCount quantum has been exhausted). This strategy prevents additional @@ -357,10 +353,7 @@ public int getWriteBufferHighWaterMark() { @Override public ChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { - if (writeBufferHighWaterMark < 0) { - throw new IllegalArgumentException( - "writeBufferHighWaterMark must be >= 0"); - } + checkPositiveOrZero(writeBufferHighWaterMark, "writeBufferHighWaterMark"); for (;;) { WriteBufferWaterMark waterMark = writeBufferWaterMark; if (writeBufferHighWaterMark < waterMark.low()) { @@ -383,10 +376,7 @@ public int getWriteBufferLowWaterMark() { @Override public ChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { - if (writeBufferLowWaterMark < 0) { - throw new IllegalArgumentException( - "writeBufferLowWaterMark must be >= 0"); - } + checkPositiveOrZero(writeBufferLowWaterMark, "writeBufferLowWaterMark"); for (;;) { WriteBufferWaterMark waterMark = writeBufferWaterMark; if (writeBufferLowWaterMark > waterMark.high()) { diff --git a/transport/src/main/java/io/netty/channel/DefaultFileRegion.java b/transport/src/main/java/io/netty/channel/DefaultFileRegion.java index 0e8d4860c38c..2435b20a313b 100644 --- a/transport/src/main/java/io/netty/channel/DefaultFileRegion.java +++ b/transport/src/main/java/io/netty/channel/DefaultFileRegion.java @@ -15,6 +15,8 @@ */ package io.netty.channel; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.util.AbstractReferenceCounted; import io.netty.util.IllegalReferenceCountException; import io.netty.util.internal.logging.InternalLogger; @@ -52,12 +54,8 @@ public DefaultFileRegion(FileChannel file, long position, long count) { if (file == null) { throw new NullPointerException("file"); } - if (position < 0) { - throw new IllegalArgumentException("position must be >= 0 but was " + position); - } - if (count < 0) { - throw new IllegalArgumentException("count must be >= 0 but was " + count); - } + checkPositiveOrZero(position, "position"); + checkPositiveOrZero(count, "count"); this.file = file; this.position = position; this.count = count; @@ -76,12 +74,8 @@ public DefaultFileRegion(File f, long position, long count) { if (f == null) { throw new NullPointerException("f"); } - if (position < 0) { - throw new IllegalArgumentException("position must be >= 0 but was " + position); - } - if (count < 0) { - throw new IllegalArgumentException("count must be >= 0 but was " + count); - } + checkPositiveOrZero(position, "position"); + checkPositiveOrZero(count, "count"); this.position = position; this.count = count; this.f = f; diff --git a/transport/src/main/java/io/netty/channel/DefaultMaxBytesRecvByteBufAllocator.java b/transport/src/main/java/io/netty/channel/DefaultMaxBytesRecvByteBufAllocator.java index 9077551f88b8..24f40b5d5485 100644 --- a/transport/src/main/java/io/netty/channel/DefaultMaxBytesRecvByteBufAllocator.java +++ b/transport/src/main/java/io/netty/channel/DefaultMaxBytesRecvByteBufAllocator.java @@ -15,6 +15,8 @@ */ package io.netty.channel; +import static io.netty.util.internal.ObjectUtil.checkPositive; + import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.util.UncheckedBooleanSupplier; @@ -124,9 +126,7 @@ public int maxBytesPerRead() { @Override public DefaultMaxBytesRecvByteBufAllocator maxBytesPerRead(int maxBytesPerRead) { - if (maxBytesPerRead <= 0) { - throw new IllegalArgumentException("maxBytesPerRead: " + maxBytesPerRead + " (expected: > 0)"); - } + checkPositive(maxBytesPerRead, "maxBytesPerRead"); // There is a dependency between this.maxBytesPerRead and this.maxBytesPerIndividualRead (a < b). // Write operations must be synchronized, but independent read operations can just be volatile. synchronized (this) { @@ -149,10 +149,7 @@ public int maxBytesPerIndividualRead() { @Override public DefaultMaxBytesRecvByteBufAllocator maxBytesPerIndividualRead(int maxBytesPerIndividualRead) { - if (maxBytesPerIndividualRead <= 0) { - throw new IllegalArgumentException( - "maxBytesPerIndividualRead: " + maxBytesPerIndividualRead + " (expected: > 0)"); - } + checkPositive(maxBytesPerIndividualRead, "maxBytesPerIndividualRead"); // There is a dependency between this.maxBytesPerRead and this.maxBytesPerIndividualRead (a < b). // Write operations must be synchronized, but independent read operations can just be volatile. synchronized (this) { @@ -174,13 +171,8 @@ public synchronized Entry maxBytesPerReadPair() { } private static void checkMaxBytesPerReadPair(int maxBytesPerRead, int maxBytesPerIndividualRead) { - if (maxBytesPerRead <= 0) { - throw new IllegalArgumentException("maxBytesPerRead: " + maxBytesPerRead + " (expected: > 0)"); - } - if (maxBytesPerIndividualRead <= 0) { - throw new IllegalArgumentException( - "maxBytesPerIndividualRead: " + maxBytesPerIndividualRead + " (expected: > 0)"); - } + checkPositive(maxBytesPerRead, "maxBytesPerRead"); + checkPositive(maxBytesPerIndividualRead, "maxBytesPerIndividualRead"); if (maxBytesPerRead < maxBytesPerIndividualRead) { throw new IllegalArgumentException( "maxBytesPerRead cannot be less than " + diff --git a/transport/src/main/java/io/netty/channel/DefaultMaxMessagesRecvByteBufAllocator.java b/transport/src/main/java/io/netty/channel/DefaultMaxMessagesRecvByteBufAllocator.java index 0a44822c56fb..2e13606320d3 100644 --- a/transport/src/main/java/io/netty/channel/DefaultMaxMessagesRecvByteBufAllocator.java +++ b/transport/src/main/java/io/netty/channel/DefaultMaxMessagesRecvByteBufAllocator.java @@ -15,6 +15,8 @@ */ package io.netty.channel; +import static io.netty.util.internal.ObjectUtil.checkPositive; + import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.util.UncheckedBooleanSupplier; @@ -42,9 +44,7 @@ public int maxMessagesPerRead() { @Override public MaxMessagesRecvByteBufAllocator maxMessagesPerRead(int maxMessagesPerRead) { - if (maxMessagesPerRead <= 0) { - throw new IllegalArgumentException("maxMessagesPerRead: " + maxMessagesPerRead + " (expected: > 0)"); - } + checkPositive(maxMessagesPerRead, "maxMessagesPerRead"); this.maxMessagesPerRead = maxMessagesPerRead; return this; } diff --git a/transport/src/main/java/io/netty/channel/DefaultMessageSizeEstimator.java b/transport/src/main/java/io/netty/channel/DefaultMessageSizeEstimator.java index 1459743259c8..cedbb2ad4228 100644 --- a/transport/src/main/java/io/netty/channel/DefaultMessageSizeEstimator.java +++ b/transport/src/main/java/io/netty/channel/DefaultMessageSizeEstimator.java @@ -15,6 +15,8 @@ */ package io.netty.channel; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; @@ -59,9 +61,7 @@ public int size(Object msg) { * @param unknownSize The size which is returned for unknown messages. */ public DefaultMessageSizeEstimator(int unknownSize) { - if (unknownSize < 0) { - throw new IllegalArgumentException("unknownSize: " + unknownSize + " (expected: >= 0)"); - } + checkPositiveOrZero(unknownSize, "unknownSize"); handle = new HandleImpl(unknownSize); } diff --git a/transport/src/main/java/io/netty/channel/FixedRecvByteBufAllocator.java b/transport/src/main/java/io/netty/channel/FixedRecvByteBufAllocator.java index bcc091ea7e2e..8bdd43df81bf 100644 --- a/transport/src/main/java/io/netty/channel/FixedRecvByteBufAllocator.java +++ b/transport/src/main/java/io/netty/channel/FixedRecvByteBufAllocator.java @@ -15,6 +15,8 @@ */ package io.netty.channel; +import static io.netty.util.internal.ObjectUtil.checkPositive; + /** * The {@link RecvByteBufAllocator} that always yields the same buffer * size prediction. This predictor ignores the feed back from the I/O thread. @@ -41,10 +43,7 @@ public int guess() { * the specified buffer size. */ public FixedRecvByteBufAllocator(int bufferSize) { - if (bufferSize <= 0) { - throw new IllegalArgumentException( - "bufferSize must greater than 0: " + bufferSize); - } + checkPositive(bufferSize, "bufferSize"); this.bufferSize = bufferSize; } diff --git a/transport/src/main/java/io/netty/channel/WriteBufferWaterMark.java b/transport/src/main/java/io/netty/channel/WriteBufferWaterMark.java index ee3d4666a153..3deb74f6bd53 100644 --- a/transport/src/main/java/io/netty/channel/WriteBufferWaterMark.java +++ b/transport/src/main/java/io/netty/channel/WriteBufferWaterMark.java @@ -15,6 +15,8 @@ */ package io.netty.channel; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + /** * WriteBufferWaterMark is used to set low water mark and high water mark for the write buffer. *

    @@ -54,9 +56,7 @@ public WriteBufferWaterMark(int low, int high) { */ WriteBufferWaterMark(int low, int high, boolean validate) { if (validate) { - if (low < 0) { - throw new IllegalArgumentException("write buffer's low water mark must be >= 0"); - } + checkPositiveOrZero(low, "low"); if (high < low) { throw new IllegalArgumentException( "write buffer's high water mark cannot be less than " + diff --git a/transport/src/main/java/io/netty/channel/socket/DefaultServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/DefaultServerSocketChannelConfig.java index 57ac53dafd25..10c7bcb700ff 100644 --- a/transport/src/main/java/io/netty/channel/socket/DefaultServerSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/DefaultServerSocketChannelConfig.java @@ -31,6 +31,7 @@ import static io.netty.channel.ChannelOption.SO_BACKLOG; import static io.netty.channel.ChannelOption.SO_RCVBUF; import static io.netty.channel.ChannelOption.SO_REUSEADDR; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; /** * The default {@link ServerSocketChannelConfig} implementation. @@ -141,9 +142,7 @@ public int getBacklog() { @Override public ServerSocketChannelConfig setBacklog(int backlog) { - if (backlog < 0) { - throw new IllegalArgumentException("backlog: " + backlog); - } + checkPositiveOrZero(backlog, "backlog"); this.backlog = backlog; return this; } From 91d3920aa298ea536be7b196f16b32b6ddd27f8d Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 31 Jan 2019 20:27:47 +0100 Subject: [PATCH 364/417] =?UTF-8?q?HttpObjectDecoder=20ignores=20HTTP=20tr?= =?UTF-8?q?ailer=20header=20when=20empty=20line=20is=20rece=E2=80=A6=20(#8?= =?UTF-8?q?799)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * HttpObjectDecoder ignores HTTP trailer header when empty line is received in seperate ByteBuf Motivation: When the empty line that termines the trailers was sent in a seperate ByteBuf we did ignore the previous parsed trailers and just returned none. Modifications: - Correct respect previous parsed trailers. - Add unit test. Result: Fixes https://github.com/netty/netty/issues/8736 --- .../handler/codec/http/HttpObjectDecoder.java | 75 ++++++++++--------- .../codec/http/HttpResponseDecoderTest.java | 29 +++++++ 2 files changed, 67 insertions(+), 37 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index af1d642a0397..f8220cdb04eb 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -640,49 +640,50 @@ private LastHttpContent readTrailingHeaders(ByteBuf buffer) { if (line == null) { return null; } + LastHttpContent trailer = this.trailer; + if (line.length() == 0 && trailer == null) { + // We have received the empty line which signals the trailer is complete and did not parse any trailers + // before. Just return an empty last content to reduce allocations. + return LastHttpContent.EMPTY_LAST_CONTENT; + } + CharSequence lastHeader = null; - if (line.length() > 0) { - LastHttpContent trailer = this.trailer; - if (trailer == null) { - trailer = this.trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders); - } - do { - char firstChar = line.charAt(0); - if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) { - List current = trailer.trailingHeaders().getAll(lastHeader); - if (!current.isEmpty()) { - int lastPos = current.size() - 1; - //please do not make one line from below code - //as it breaks +XX:OptimizeStringConcat optimization - String lineTrimmed = line.toString().trim(); - String currentLastPos = current.get(lastPos); - current.set(lastPos, currentLastPos + lineTrimmed); - } - } else { - splitHeader(line); - CharSequence headerName = name; - if (!HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(headerName) && + if (trailer == null) { + trailer = this.trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders); + } + while (line.length() > 0) { + char firstChar = line.charAt(0); + if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) { + List current = trailer.trailingHeaders().getAll(lastHeader); + if (!current.isEmpty()) { + int lastPos = current.size() - 1; + //please do not make one line from below code + //as it breaks +XX:OptimizeStringConcat optimization + String lineTrimmed = line.toString().trim(); + String currentLastPos = current.get(lastPos); + current.set(lastPos, currentLastPos + lineTrimmed); + } + } else { + splitHeader(line); + CharSequence headerName = name; + if (!HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(headerName) && !HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(headerName) && !HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(headerName)) { - trailer.trailingHeaders().add(headerName, value); - } - lastHeader = name; - // reset name and value fields - name = null; - value = null; - } - - line = headerParser.parse(buffer); - if (line == null) { - return null; + trailer.trailingHeaders().add(headerName, value); } - } while (line.length() > 0); - - this.trailer = null; - return trailer; + lastHeader = name; + // reset name and value fields + name = null; + value = null; + } + line = headerParser.parse(buffer); + if (line == null) { + return null; + } } - return LastHttpContent.EMPTY_LAST_CONTENT; + this.trailer = null; + return trailer; } protected abstract boolean isDecodingRequest(); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java index 017dbd5ff943..f062da2bf1f4 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java @@ -683,4 +683,33 @@ public void testConnectionClosedBeforeHeadersReceived() { assertThat(message.decoderResult().cause(), instanceOf(PrematureChannelClosureException.class)); assertNull(channel.readInbound()); } + + @Test + public void testTrailerWithEmptyLineInSeparateBuffer() { + HttpResponseDecoder decoder = new HttpResponseDecoder(); + EmbeddedChannel channel = new EmbeddedChannel(decoder); + + String headers = "HTTP/1.1 200 OK\r\n" + + "Transfer-Encoding: chunked\r\n" + + "Trailer: My-Trailer\r\n"; + assertFalse(channel.writeInbound(Unpooled.copiedBuffer(headers.getBytes(CharsetUtil.US_ASCII)))); + assertTrue(channel.writeInbound(Unpooled.copiedBuffer("\r\n".getBytes(CharsetUtil.US_ASCII)))); + + assertTrue(channel.writeInbound(Unpooled.copiedBuffer("0\r\n", CharsetUtil.US_ASCII))); + assertTrue(channel.writeInbound(Unpooled.copiedBuffer("My-Trailer: 42\r\n", CharsetUtil.US_ASCII))); + assertTrue(channel.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII))); + + HttpResponse response = channel.readInbound(); + assertEquals(2, response.headers().size()); + assertEquals("chunked", response.headers().get(HttpHeaderNames.TRANSFER_ENCODING)); + assertEquals("My-Trailer", response.headers().get(HttpHeaderNames.TRAILER)); + + LastHttpContent lastContent = channel.readInbound(); + assertEquals(1, lastContent.trailingHeaders().size()); + assertEquals("42", lastContent.trailingHeaders().get("My-Trailer")); + assertEquals(0, lastContent.content().readableBytes()); + lastContent.release(); + + assertFalse(channel.finish()); + } } From ad922fa47e1c6af9218836730e13d438690a71e5 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 1 Feb 2019 10:23:54 +0100 Subject: [PATCH 365/417] Mark ChannelHandlerAdapter.exceptionCaught(...) as @deprecated. (#8826) Motivation: 41e03adf249ee9d23938fecf9be980a096710b36 marked ChannelHandler.exceptionCaught(...) as @deprecated but missed to also mark ChannelHandlerAdapter.exceptionCaught(...) as @deprecated. We should do so as most people extend the base classes and not implement the interfaces directly. Modifications: Mark ChannelHandlerAdapter.exceptionCaught(...) as @deprecated as well. Result: Mark method as @deprecated to warn users about its removal. --- .../src/main/java/io/netty/channel/ChannelHandlerAdapter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/transport/src/main/java/io/netty/channel/ChannelHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelHandlerAdapter.java index aadc691d669a..ee380b584874 100644 --- a/transport/src/main/java/io/netty/channel/ChannelHandlerAdapter.java +++ b/transport/src/main/java/io/netty/channel/ChannelHandlerAdapter.java @@ -81,8 +81,11 @@ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. + * + * @deprecated is part of {@link ChannelInboundHandler} */ @Override + @Deprecated public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.fireExceptionCaught(cause); } From 7bba4f49cf133384e2c368096994a1d891f60274 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 1 Feb 2019 10:29:36 +0100 Subject: [PATCH 366/417] Reduce GC produced by native DatagramChannel implementations when in connected mode. (#8806) Motivation: In the native code EpollDatagramChannel / KQueueDatagramChannel creates a DatagramSocketAddress object for each received UDP datagram even when in connected mode as it uses the recvfrom(...) / recvmsg(...) method. Creating these is quite heavy in terms of allocations as internally, char[], String, Inet4Address, InetAddressHolder, InetSocketAddressHolder, InetAddress[], byte[] objects are getting generated when constructing the object. When in connected mode we can just use regular read(...) calls which do not need to allocate all of these. Modifications: - When in connected mode use read(...) and NOT recvfrom(..) / readmsg(...) to reduce allocations when possible. - Adjust tests to ensure read works as expected when in connected mode. Result: Less allocations and GC when using native datagram channels in connected mode. Fixes https://github.com/netty/netty/issues/8770. --- .../transport/socket/DatagramUnicastTest.java | 32 ++++++-- .../channel/epoll/EpollDatagramChannel.java | 82 +++++++++++++------ .../channel/kqueue/KQueueDatagramChannel.java | 81 ++++++++++++------ 3 files changed, 135 insertions(+), 60 deletions(-) diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java index b49f6780ef52..37adc20e3837 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java @@ -158,7 +158,7 @@ public void channelRead0(ChannelHandlerContext ctx, Object msgs) throws Exceptio }); final CountDownLatch latch = new CountDownLatch(count); - sc = setupServerChannel(sb, bytes, latch); + sc = setupServerChannel(sb, bytes, latch, false); if (bindClient) { cc = cb.bind(newSocketAddress()).sync().channel(); } else { @@ -209,10 +209,21 @@ private void testSimpleSendWithConnect(Bootstrap sb, Bootstrap cb, ByteBuf buf, private void testSimpleSendWithConnect0(Bootstrap sb, Bootstrap cb, ByteBuf buf, final byte[] bytes, int count, WrapType wrapType) throws Throwable { - cb.handler(new SimpleChannelInboundHandler() { + final CountDownLatch clientLatch = new CountDownLatch(count); + + cb.handler(new SimpleChannelInboundHandler() { @Override - public void channelRead0(ChannelHandlerContext ctx, Object msgs) throws Exception { - // Nothing will be sent. + public void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { + ByteBuf buf = msg.content(); + assertEquals(bytes.length, buf.readableBytes()); + for (int i = 0; i < bytes.length; i++) { + assertEquals(bytes[i], buf.getByte(buf.readerIndex() + i)); + } + + // Test that the channel's localAddress is equal to the message's recipient + assertEquals(ctx.channel().localAddress(), msg.recipient()); + + clientLatch.countDown(); } }); @@ -220,7 +231,7 @@ public void channelRead0(ChannelHandlerContext ctx, Object msgs) throws Exceptio DatagramChannel cc = null; try { final CountDownLatch latch = new CountDownLatch(count); - sc = setupServerChannel(sb, bytes, latch); + sc = setupServerChannel(sb, bytes, latch, true); cc = (DatagramChannel) cb.connect(sc.localAddress()).sync().channel(); for (int i = 0; i < count; i++) { @@ -243,7 +254,7 @@ public void channelRead0(ChannelHandlerContext ctx, Object msgs) throws Exceptio } cc.flush(); assertTrue(latch.await(10, TimeUnit.SECONDS)); - + assertTrue(clientLatch.await(10, TimeUnit.SECONDS)); assertTrue(cc.isConnected()); // Test what happens when we call disconnect() @@ -264,7 +275,7 @@ public void channelRead0(ChannelHandlerContext ctx, Object msgs) throws Exceptio } @SuppressWarnings("deprecation") - private Channel setupServerChannel(Bootstrap sb, final byte[] bytes, final CountDownLatch latch) + private Channel setupServerChannel(Bootstrap sb, final byte[] bytes, final CountDownLatch latch, final boolean echo) throws Throwable { sb.handler(new ChannelInitializer() { @Override @@ -274,13 +285,16 @@ protected void initChannel(Channel ch) throws Exception { public void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { ByteBuf buf = msg.content(); assertEquals(bytes.length, buf.readableBytes()); - for (byte b : bytes) { - assertEquals(b, buf.readByte()); + for (int i = 0; i < bytes.length; i++) { + assertEquals(bytes[i], buf.getByte(buf.readerIndex() + i)); } // Test that the channel's localAddress is equal to the message's recipient assertEquals(ctx.channel().localAddress(), msg.recipient()); + if (echo) { + ctx.writeAndFlush(new DatagramPacket(buf.retainedDuplicate(), msg.sender())); + } latch.countDown(); } }); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java index 714f612634b1..7679404fdd78 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java @@ -28,6 +28,7 @@ import io.netty.channel.socket.DatagramChannelConfig; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.unix.DatagramSocketAddress; +import io.netty.channel.unix.Errors; import io.netty.channel.unix.IovArray; import io.netty.channel.unix.UnixChannelUtil; import io.netty.util.internal.StringUtil; @@ -36,6 +37,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; +import java.net.PortUnreachableException; import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; @@ -449,46 +451,72 @@ void epollInReady() { Throwable exception = null; try { - ByteBuf data = null; + ByteBuf byteBuf = null; try { + boolean connected = isConnected(); do { - data = allocHandle.allocate(allocator); - allocHandle.attemptedBytesRead(data.writableBytes()); - final DatagramSocketAddress remoteAddress; - if (data.hasMemoryAddress()) { - // has a memory address so use optimized call - remoteAddress = socket.recvFromAddress(data.memoryAddress(), data.writerIndex(), - data.capacity()); + byteBuf = allocHandle.allocate(allocator); + allocHandle.attemptedBytesRead(byteBuf.writableBytes()); + + final DatagramPacket packet; + if (connected) { + try { + allocHandle.lastBytesRead(doReadBytes(byteBuf)); + } catch (Errors.NativeIoException e) { + // We need to correctly translate connect errors to match NIO behaviour. + if (e.expectedErr() == Errors.ERROR_ECONNREFUSED_NEGATIVE) { + PortUnreachableException error = new PortUnreachableException(e.getMessage()); + error.initCause(e); + throw error; + } + throw e; + } + if (allocHandle.lastBytesRead() <= 0) { + // nothing was read, release the buffer. + byteBuf.release(); + byteBuf = null; + break; + } + packet = new DatagramPacket( + byteBuf, (InetSocketAddress) localAddress(), (InetSocketAddress) remoteAddress()); } else { - ByteBuffer nioData = data.internalNioBuffer(data.writerIndex(), data.writableBytes()); - remoteAddress = socket.recvFrom(nioData, nioData.position(), nioData.limit()); - } + final DatagramSocketAddress remoteAddress; + if (byteBuf.hasMemoryAddress()) { + // has a memory address so use optimized call + remoteAddress = socket.recvFromAddress(byteBuf.memoryAddress(), byteBuf.writerIndex(), + byteBuf.capacity()); + } else { + ByteBuffer nioData = byteBuf.internalNioBuffer( + byteBuf.writerIndex(), byteBuf.writableBytes()); + remoteAddress = socket.recvFrom(nioData, nioData.position(), nioData.limit()); + } - if (remoteAddress == null) { - allocHandle.lastBytesRead(-1); - data.release(); - data = null; - break; - } + if (remoteAddress == null) { + allocHandle.lastBytesRead(-1); + byteBuf.release(); + byteBuf = null; + break; + } + InetSocketAddress localAddress = remoteAddress.localAddress(); + if (localAddress == null) { + localAddress = (InetSocketAddress) localAddress(); + } + allocHandle.lastBytesRead(remoteAddress.receivedAmount()); + byteBuf.writerIndex(byteBuf.writerIndex() + allocHandle.lastBytesRead()); - InetSocketAddress localAddress = remoteAddress.localAddress(); - if (localAddress == null) { - localAddress = (InetSocketAddress) localAddress(); + packet = new DatagramPacket(byteBuf, localAddress, remoteAddress); } allocHandle.incMessagesRead(1); - allocHandle.lastBytesRead(remoteAddress.receivedAmount()); - data.writerIndex(data.writerIndex() + allocHandle.lastBytesRead()); readPending = false; - pipeline.fireChannelRead( - new DatagramPacket(data, localAddress, remoteAddress)); + pipeline.fireChannelRead(packet); - data = null; + byteBuf = null; } while (allocHandle.continueReading()); } catch (Throwable t) { - if (data != null) { - data.release(); + if (byteBuf != null) { + byteBuf.release(); } exception = t; } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueDatagramChannel.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueDatagramChannel.java index 887951a28f77..b2925c58c2c4 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueDatagramChannel.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueDatagramChannel.java @@ -28,6 +28,7 @@ import io.netty.channel.socket.DatagramChannelConfig; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.unix.DatagramSocketAddress; +import io.netty.channel.unix.Errors; import io.netty.channel.unix.IovArray; import io.netty.channel.unix.UnixChannelUtil; import io.netty.util.internal.StringUtil; @@ -37,6 +38,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; +import java.net.PortUnreachableException; import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; @@ -420,41 +422,72 @@ void readReady(KQueueRecvByteAllocatorHandle allocHandle) { Throwable exception = null; try { - ByteBuf data = null; + ByteBuf byteBuf = null; try { + boolean connected = isConnected(); do { - data = allocHandle.allocate(allocator); - allocHandle.attemptedBytesRead(data.writableBytes()); - final DatagramSocketAddress remoteAddress; - if (data.hasMemoryAddress()) { - // has a memory address so use optimized call - remoteAddress = socket.recvFromAddress(data.memoryAddress(), data.writerIndex(), - data.capacity()); + byteBuf = allocHandle.allocate(allocator); + allocHandle.attemptedBytesRead(byteBuf.writableBytes()); + + final DatagramPacket packet; + if (connected) { + try { + allocHandle.lastBytesRead(doReadBytes(byteBuf)); + } catch (Errors.NativeIoException e) { + // We need to correctly translate connect errors to match NIO behaviour. + if (e.expectedErr() == Errors.ERROR_ECONNREFUSED_NEGATIVE) { + PortUnreachableException error = new PortUnreachableException(e.getMessage()); + error.initCause(e); + throw error; + } + throw e; + } + if (allocHandle.lastBytesRead() <= 0) { + // nothing was read, release the buffer. + byteBuf.release(); + byteBuf = null; + break; + } + packet = new DatagramPacket(byteBuf, + (InetSocketAddress) localAddress(), (InetSocketAddress) remoteAddress()); } else { - ByteBuffer nioData = data.internalNioBuffer(data.writerIndex(), data.writableBytes()); - remoteAddress = socket.recvFrom(nioData, nioData.position(), nioData.limit()); - } - - if (remoteAddress == null) { - allocHandle.lastBytesRead(-1); - data.release(); - data = null; - break; + final DatagramSocketAddress remoteAddress; + if (byteBuf.hasMemoryAddress()) { + // has a memory address so use optimized call + remoteAddress = socket.recvFromAddress(byteBuf.memoryAddress(), byteBuf.writerIndex(), + byteBuf.capacity()); + } else { + ByteBuffer nioData = byteBuf.internalNioBuffer( + byteBuf.writerIndex(), byteBuf.writableBytes()); + remoteAddress = socket.recvFrom(nioData, nioData.position(), nioData.limit()); + } + + if (remoteAddress == null) { + allocHandle.lastBytesRead(-1); + byteBuf.release(); + byteBuf = null; + break; + } + InetSocketAddress localAddress = remoteAddress.localAddress(); + if (localAddress == null) { + localAddress = (InetSocketAddress) localAddress(); + } + allocHandle.lastBytesRead(remoteAddress.receivedAmount()); + byteBuf.writerIndex(byteBuf.writerIndex() + allocHandle.lastBytesRead()); + + packet = new DatagramPacket(byteBuf, localAddress, remoteAddress); } allocHandle.incMessagesRead(1); - allocHandle.lastBytesRead(remoteAddress.receivedAmount()); - data.writerIndex(data.writerIndex() + allocHandle.lastBytesRead()); readPending = false; - pipeline.fireChannelRead( - new DatagramPacket(data, (InetSocketAddress) localAddress(), remoteAddress)); + pipeline.fireChannelRead(packet); - data = null; + byteBuf = null; } while (allocHandle.continueReading()); } catch (Throwable t) { - if (data != null) { - data.release(); + if (byteBuf != null) { + byteBuf.release(); } exception = t; } From 98aa5fbd66093bbb3b9ed6e6724586074d6f804f Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Fri, 1 Feb 2019 01:31:53 -0800 Subject: [PATCH 367/417] CompositeByteBuf tidy-up (#8784) Motivation There's some miscellaneous cleanup/simplification of CompositeByteBuf which would help make the code a bit clearer. Modifications - Simplify web of constructors and addComponents methods, reducing duplication of logic - Rename `Component.freeIfNecessary()` method to just `free()`, which is less confusing (see #8641) - Make loop in addComponents0(...) method more verbose/readable (see https://github.com/netty/netty/pull/8437#discussion_r232124414) - Simplify addition/subtraction in setBytes(...) methods Result Smaller/clearer code --- .../io/netty/buffer/CompositeByteBuf.java | 94 ++++++++----------- 1 file changed, 40 insertions(+), 54 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 43c12dc6bd7b..ab4cb577b973 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -96,8 +96,7 @@ public CompositeByteBuf( this(alloc, direct, maxNumComponents, buffers instanceof Collection ? ((Collection) buffers).size() : 0); - addComponents0(false, 0, buffers); - consolidateIfNeeded(); + addComponents(false, 0, buffers); setIndex(0, capacity()); } @@ -220,10 +219,7 @@ public CompositeByteBuf addComponent(int cIndex, ByteBuf buffer) { * {@link CompositeByteBuf}. */ public CompositeByteBuf addComponent(boolean increaseWriterIndex, ByteBuf buffer) { - checkNotNull(buffer, "buffer"); - addComponent0(increaseWriterIndex, componentCount, buffer); - consolidateIfNeeded(); - return this; + return addComponent(increaseWriterIndex, componentCount, buffer); } /** @@ -252,9 +248,7 @@ public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... bu * ownership of all {@link ByteBuf} objects is transferred to this {@link CompositeByteBuf}. */ public CompositeByteBuf addComponents(boolean increaseWriterIndex, Iterable buffers) { - addComponents0(increaseWriterIndex, componentCount, buffers); - consolidateIfNeeded(); - return this; + return addComponents(increaseWriterIndex, componentCount, buffers); } /** @@ -304,7 +298,6 @@ private int addComponent0(boolean increaseWriterIndex, int cIndex, ByteBuf buffe } } - // unwrap if already sliced @SuppressWarnings("deprecation") private Component newComponent(ByteBuf buf, int offset) { if (checkAccessible && buf.refCnt() == 0) { @@ -312,6 +305,7 @@ private Component newComponent(ByteBuf buf, int offset) { } int srcIndex = buf.readerIndex(), len = buf.readableBytes(); ByteBuf slice = null; + // unwrap if already sliced if (buf instanceof AbstractUnpooledSlicedByteBuf) { srcIndex += ((AbstractUnpooledSlicedByteBuf) buf).idx(0); slice = buf; @@ -345,20 +339,25 @@ public CompositeByteBuf addComponents(int cIndex, ByteBuf... buffers) { return this; } - private int addComponents0(boolean increaseWriterIndex, final int cIndex, ByteBuf[] buffers, int arrOffset) { + private CompositeByteBuf addComponents0(boolean increaseWriterIndex, + final int cIndex, ByteBuf[] buffers, int arrOffset) { final int len = buffers.length, count = len - arrOffset; + // only set ci after we've shifted so that finally block logic is always correct int ci = Integer.MAX_VALUE; try { checkComponentIndex(cIndex); shiftComps(cIndex, count); // will increase componentCount - ci = cIndex; // only set this after we've shifted so that finally block logic is always correct int nextOffset = cIndex > 0 ? components[cIndex - 1].endOffset : 0; - for (ByteBuf b; arrOffset < len && (b = buffers[arrOffset]) != null; arrOffset++, ci++) { + for (ci = cIndex; arrOffset < len; arrOffset++, ci++) { + ByteBuf b = buffers[arrOffset]; + if (b == null) { + break; + } Component c = newComponent(b, nextOffset); components[ci] = c; nextOffset = c.endOffset; } - return ci; + return this; } finally { // ci is now the index following the last successfully added component if (ci < componentCount) { @@ -379,7 +378,6 @@ private int addComponents0(boolean increaseWriterIndex, final int cIndex, ByteBu private int addComponents0(boolean increaseWriterIndex, int cIndex, ByteWrapper wrapper, T[] buffers, int offset) { - checkNotNull(buffers, "buffers"); checkComponentIndex(cIndex); // No need for consolidation @@ -413,18 +411,16 @@ private int addComponents0(boolean increaseWriterIndex, int cIndex, * {@link CompositeByteBuf}. */ public CompositeByteBuf addComponents(int cIndex, Iterable buffers) { - addComponents0(false, cIndex, buffers); - consolidateIfNeeded(); - return this; + return addComponents(false, cIndex, buffers); } // TODO optimize further, similar to ByteBuf[] version // (difference here is that we don't know *always* know precise size increase in advance, // but we do in the most common case that the Iterable is a Collection) - private int addComponents0(boolean increaseIndex, int cIndex, Iterable buffers) { + private CompositeByteBuf addComponents(boolean increaseIndex, int cIndex, Iterable buffers) { if (buffers instanceof ByteBuf) { // If buffers also implements ByteBuf (e.g. CompositeByteBuf), it has to go to addComponent(ByteBuf). - return addComponent0(increaseIndex, cIndex, (ByteBuf) buffers); + return addComponent(increaseIndex, cIndex, (ByteBuf) buffers); } checkNotNull(buffers, "buffers"); Iterator it = buffers.iterator(); @@ -440,12 +436,13 @@ private int addComponents0(boolean increaseIndex, int cIndex, Iterable cIndex = addComponent0(increaseIndex, cIndex, b) + 1; cIndex = Math.min(cIndex, componentCount); } - return cIndex; } finally { while (it.hasNext()) { ReferenceCountUtil.safeRelease(it.next()); } } + consolidateIfNeeded(); + return this; } /** @@ -516,7 +513,7 @@ public CompositeByteBuf removeComponent(int cIndex) { if (lastAccessed == comp) { lastAccessed = null; } - comp.freeIfNecessary(); + comp.free(); removeComp(cIndex); if (comp.length() > 0) { // Only need to call updateComponentOffsets if the length was > 0 @@ -547,7 +544,7 @@ public CompositeByteBuf removeComponents(int cIndex, int numComponents) { if (lastAccessed == c) { lastAccessed = null; } - c.freeIfNecessary(); + c.free(); } removeCompRange(cIndex, endIndex); @@ -759,7 +756,7 @@ public CompositeByteBuf capacity(int newCapacity) { c.slice = null; break; } - c.freeIfNecessary(); + c.free(); bytesToTrim -= cLength; } removeCompRange(i + 1, size); @@ -1305,15 +1302,11 @@ public int setBytes(int index, InputStream in, int length) throws IOException { } } + index += localReadBytes; + length -= localReadBytes; + readBytes += localReadBytes; if (localReadBytes == localLength) { - index += localLength; - length -= localLength; - readBytes += localLength; i ++; - } else { - index += localReadBytes; - length -= localReadBytes; - readBytes += localReadBytes; } } while (length > 0); @@ -1351,15 +1344,11 @@ public int setBytes(int index, ScatteringByteChannel in, int length) throws IOEx } } + index += localReadBytes; + length -= localReadBytes; + readBytes += localReadBytes; if (localReadBytes == localLength) { - index += localLength; - length -= localLength; - readBytes += localLength; i ++; - } else { - index += localReadBytes; - length -= localReadBytes; - readBytes += localReadBytes; } } while (length > 0); @@ -1397,15 +1386,11 @@ public int setBytes(int index, FileChannel in, long position, int length) throws } } + index += localReadBytes; + length -= localReadBytes; + readBytes += localReadBytes; if (localReadBytes == localLength) { - index += localLength; - length -= localLength; - readBytes += localLength; i ++; - } else { - index += localReadBytes; - length -= localReadBytes; - readBytes += localReadBytes; } } while (length > 0); @@ -1677,7 +1662,7 @@ public CompositeByteBuf discardReadComponents() { int writerIndex = writerIndex(); if (readerIndex == writerIndex && writerIndex == capacity()) { for (int i = 0, size = componentCount; i < size; i++) { - components[i].freeIfNecessary(); + components[i].free(); } lastAccessed = null; clearComps(); @@ -1689,7 +1674,7 @@ public CompositeByteBuf discardReadComponents() { // Remove read components. int firstComponentId = toComponentIndex0(readerIndex); for (int i = 0; i < firstComponentId; i ++) { - components[i].freeIfNecessary(); + components[i].free(); } lastAccessed = null; removeCompRange(0, firstComponentId); @@ -1715,7 +1700,7 @@ public CompositeByteBuf discardReadBytes() { int writerIndex = writerIndex(); if (readerIndex == writerIndex && writerIndex == capacity()) { for (int i = 0, size = componentCount; i < size; i++) { - components[i].freeIfNecessary(); + components[i].free(); } lastAccessed = null; clearComps(); @@ -1728,7 +1713,7 @@ public CompositeByteBuf discardReadBytes() { int firstComponentId = toComponentIndex0(readerIndex); for (int i = 0; i < firstComponentId; i ++) { Component c = components[i]; - c.freeIfNecessary(); + c.free(); if (lastAccessed == c) { lastAccessed = null; } @@ -1738,7 +1723,7 @@ public CompositeByteBuf discardReadBytes() { Component c = components[firstComponentId]; if (readerIndex == c.endOffset) { // new slice would be empty, so remove instead - c.freeIfNecessary(); + c.free(); if (lastAccessed == c) { lastAccessed = null; } @@ -1804,7 +1789,7 @@ void reposition(int newOffset) { // copy then release void transferTo(ByteBuf dst) { dst.writeBytes(buf, idx(offset), length()); - freeIfNecessary(); + free(); } ByteBuf slice() { @@ -1815,7 +1800,7 @@ ByteBuf duplicate() { return buf.duplicate().setIndex(idx(offset), idx(endOffset)); } - void freeIfNecessary() { + void free() { // Release the slice if present since it may have a different // refcount to the unwrapped buf if it is a PooledSlicedByteBuf ByteBuf buffer = slice; @@ -1824,7 +1809,8 @@ void freeIfNecessary() { } else { buf.release(); } - // null out in either case since it could be racy + // null out in either case since it could be racy if set lazily (but not + // in the case we care about, where it will have been set in the ctor) slice = null; } } @@ -2130,7 +2116,7 @@ protected void deallocate() { // We're not using foreach to avoid creating an iterator. // see https://github.com/netty/netty/issues/2642 for (int i = 0, size = componentCount; i < size; i++) { - components[i].freeIfNecessary(); + components[i].free(); } } From 154d6e87f6befe1d8bbb71cb58a6f844a8d4f051 Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Fri, 1 Feb 2019 22:03:03 -0800 Subject: [PATCH 368/417] Fix varargs parameter logging in LocationAwareSlf4JLogger (#8834) Motivation As pointed out by @91he in https://github.com/netty/netty/pull/8595#issuecomment-459181794, there is a remaining bug in LocationAwareSlf4JLogger following the updates done in #8595. The logging methods which take a varargs message parameter array should format using MessageFormatter.arrayFormat rather than MessageFormatter.format. Modifications Change varargs param methods in LocationAwareSlf4JLogger to use MessageFormatter.arrayFormat and extend unit test to cover these cases. Results Correct log output when logging messages with > 2 parameters when using LocationAwareSlf4JLogger. --- .../logging/LocationAwareSlf4JLogger.java | 10 +++++----- .../logging/Slf4JLoggerFactoryTest.java | 20 ++++++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java b/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java index 33eb705a8f0a..7f3628368965 100644 --- a/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java +++ b/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java @@ -79,7 +79,7 @@ public void trace(String format, Object argA, Object argB) { @Override public void trace(String format, Object... argArray) { if (isTraceEnabled()) { - log(TRACE_INT, org.slf4j.helpers.MessageFormatter.format(format, argArray)); + log(TRACE_INT, org.slf4j.helpers.MessageFormatter.arrayFormat(format, argArray)); } } @@ -119,7 +119,7 @@ public void debug(String format, Object argA, Object argB) { @Override public void debug(String format, Object... argArray) { if (isDebugEnabled()) { - log(DEBUG_INT, org.slf4j.helpers.MessageFormatter.format(format, argArray)); + log(DEBUG_INT, org.slf4j.helpers.MessageFormatter.arrayFormat(format, argArray)); } } @@ -159,7 +159,7 @@ public void info(String format, Object argA, Object argB) { @Override public void info(String format, Object... argArray) { if (isInfoEnabled()) { - log(INFO_INT, org.slf4j.helpers.MessageFormatter.format(format, argArray)); + log(INFO_INT, org.slf4j.helpers.MessageFormatter.arrayFormat(format, argArray)); } } @@ -192,7 +192,7 @@ public void warn(String format, Object arg) { @Override public void warn(String format, Object... argArray) { if (isWarnEnabled()) { - log(WARN_INT, org.slf4j.helpers.MessageFormatter.format(format, argArray)); + log(WARN_INT, org.slf4j.helpers.MessageFormatter.arrayFormat(format, argArray)); } } @@ -239,7 +239,7 @@ public void error(String format, Object argA, Object argB) { @Override public void error(String format, Object... argArray) { if (isErrorEnabled()) { - log(ERROR_INT, org.slf4j.helpers.MessageFormatter.format(format, argArray)); + log(ERROR_INT, org.slf4j.helpers.MessageFormatter.arrayFormat(format, argArray)); } } diff --git a/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java b/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java index 8cb2f84f7a90..9b08fe5397cb 100644 --- a/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java +++ b/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java @@ -71,46 +71,56 @@ public void testFormatMessage() { InternalLogger internalLogger = Slf4JLoggerFactory.wrapLogger(logger); internalLogger.debug("{}", "debug"); internalLogger.debug("{} {}", "debug1", "debug2"); + internalLogger.debug("{} {} {}", "debug1", "debug2", "debug3"); internalLogger.error("{}", "error"); internalLogger.error("{} {}", "error1", "error2"); + internalLogger.error("{} {} {}", "error1", "error2", "error3"); internalLogger.info("{}", "info"); internalLogger.info("{} {}", "info1", "info2"); + internalLogger.info("{} {} {}", "info1", "info2", "info3"); internalLogger.trace("{}", "trace"); internalLogger.trace("{} {}", "trace1", "trace2"); + internalLogger.trace("{} {} {}", "trace1", "trace2", "trace3"); internalLogger.warn("{}", "warn"); internalLogger.warn("{} {}", "warn1", "warn2"); + internalLogger.warn("{} {} {}", "warn1", "warn2", "warn3"); - verify(logger, times(2)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), + verify(logger, times(3)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), eq(LocationAwareLogger.DEBUG_INT), captor.capture(), any(Object[].class), ArgumentMatchers.isNull()); - verify(logger, times(2)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), + verify(logger, times(3)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), eq(LocationAwareLogger.ERROR_INT), captor.capture(), any(Object[].class), ArgumentMatchers.isNull()); - verify(logger, times(2)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), + verify(logger, times(3)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), eq(LocationAwareLogger.INFO_INT), captor.capture(), any(Object[].class), ArgumentMatchers.isNull()); - verify(logger, times(2)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), + verify(logger, times(3)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), eq(LocationAwareLogger.TRACE_INT), captor.capture(), any(Object[].class), ArgumentMatchers.isNull()); - verify(logger, times(2)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), + verify(logger, times(3)).log(ArgumentMatchers.isNull(), eq(LocationAwareSlf4JLogger.FQCN), eq(LocationAwareLogger.WARN_INT), captor.capture(), any(Object[].class), ArgumentMatchers.isNull()); Iterator logMessages = captor.getAllValues().iterator(); assertEquals("debug", logMessages.next()); assertEquals("debug1 debug2", logMessages.next()); + assertEquals("debug1 debug2 debug3", logMessages.next()); assertEquals("error", logMessages.next()); assertEquals("error1 error2", logMessages.next()); + assertEquals("error1 error2 error3", logMessages.next()); assertEquals("info", logMessages.next()); assertEquals("info1 info2", logMessages.next()); + assertEquals("info1 info2 info3", logMessages.next()); assertEquals("trace", logMessages.next()); assertEquals("trace1 trace2", logMessages.next()); + assertEquals("trace1 trace2 trace3", logMessages.next()); assertEquals("warn", logMessages.next()); assertEquals("warn1 warn2", logMessages.next()); + assertEquals("warn1 warn2 warn3", logMessages.next()); assertFalse(logMessages.hasNext()); } } From 7f61055cbdcd8ac1226d3572c2f38667c978f856 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 2 Feb 2019 07:10:02 +0100 Subject: [PATCH 369/417] Reduce direct memory overhead per EpollEventLoop when using EpollDatagramChannel (#8825) Motivation: When using a linux distribution that supports sendmmsg(...) we allocated enough direct memory per EpollEventLoop to be able to write IOV_MAX number of iovecs per message that can be written per sendmmsg. The number of messages that can be written per sendmmsg(...) call is limited by UIO_MAX_IOV. In practice this resulted in an allocation of 16MB direct memory per EpollEventLoop instance that stayed allocated until the EpollEventLoop was shutdown which happens as part of the shutdown of the enclosing EpollEVentLoopGroup. This resulted in quite some heavy direct memory usage in practice even when in practice we have very slim changes to ever need all of the memory. Modification: Adjust NativeDatagramPacketArray to share one IovArray instance across all NativeDatagramPacket instances it holds. This limits the max number of iovecs we can write across all messages to IOV_MAX per sendmmsg(...) call. This in practice will still be enough to allow us to write multiple messages with one syscall while keep the memory overhead to a minimum. Result: Smaller direct memory footprint per EpollEventLoop when using EpollDatagramChannel on distributions that support sendmmsg(...). Fixes https://github.com/netty/netty/issues/8814 --- .../epoll/NativeDatagramPacketArray.java | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java index 0bdb9eb69d7a..6107af2c55f7 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java @@ -33,6 +33,10 @@ final class NativeDatagramPacketArray implements ChannelOutboundBuffer.MessagePr // Use UIO_MAX_IOV as this is the maximum number we can write with one sendmmsg(...) call. private final NativeDatagramPacket[] packets = new NativeDatagramPacket[UIO_MAX_IOV]; + + // We share one IovArray for all NativeDatagramPackets to reduce memory overhead. This will allow us to write + // up to IOV_MAX iovec across all messages in one sendmmsg(...) call. + private final IovArray iovArray = new IovArray(); private int count; NativeDatagramPacketArray() { @@ -47,6 +51,8 @@ final class NativeDatagramPacketArray implements ChannelOutboundBuffer.MessagePr */ boolean add(DatagramPacket packet) { if (count == packets.length) { + // We already filled up to UIO_MAX_IOV messages. This is the max allowed per sendmmsg(...) call, we will + // try again later. return false; } ByteBuf content = packet.content(); @@ -56,9 +62,13 @@ boolean add(DatagramPacket packet) { } NativeDatagramPacket p = packets[count]; InetSocketAddress recipient = packet.recipient(); - if (!p.init(content, recipient)) { + + int offset = iovArray.count(); + if (!iovArray.add(content)) { + // Not enough space to hold the whole content, we will try again later. return false; } + p.init(iovArray.memoryAddress(offset), iovArray.count() - offset, recipient); count++; return true; @@ -85,13 +95,11 @@ NativeDatagramPacket[] packets() { void clear() { this.count = 0; + this.iovArray.clear(); } void release() { - // Release all packets - for (NativeDatagramPacket datagramPacket : packets) { - datagramPacket.release(); - } + iovArray.release(); } /** @@ -99,10 +107,6 @@ void release() { */ @SuppressWarnings("unused") static final class NativeDatagramPacket { - // Each NativeDatagramPackets holds a IovArray which is used for gathering writes. - // This is ok as NativeDatagramPacketArray is always obtained from an EpollEventLoop - // field so the memory needed is quite small anyway. - private final IovArray array = new IovArray(); // This is the actual struct iovec* private long memoryAddress; @@ -112,21 +116,9 @@ static final class NativeDatagramPacket { private int scopeId; private int port; - private void release() { - array.release(); - } - - /** - * Init this instance and return {@code true} if the init was successful. - */ - private boolean init(ByteBuf buf, InetSocketAddress recipient) { - array.clear(); - if (!array.add(buf)) { - return false; - } - // always start from offset 0 - memoryAddress = array.memoryAddress(0); - count = array.count(); + private void init(long memoryAddress, int count, InetSocketAddress recipient) { + this.memoryAddress = memoryAddress; + this.count = count; InetAddress address = recipient.getAddress(); if (address instanceof Inet6Address) { @@ -137,7 +129,6 @@ private boolean init(ByteBuf buf, InetSocketAddress recipient) { scopeId = 0; } port = recipient.getPort(); - return true; } } } From 95bc819513c333c4bf7269613ef92b8c1a51b05a Mon Sep 17 00:00:00 2001 From: Carl Mastrangelo Date: Fri, 1 Feb 2019 22:16:36 -0800 Subject: [PATCH 370/417] http-proxy: attach headers to connection exception (#8824) Motivation: When a proxy fails to connect, it includes useful error detail in the headers. Modification: - Add an HTTP Specific ProxyConnectException - Attach headers (if any) in the event of a non-200 response Result: Able to surface more useful error info to applications --- .../netty/handler/proxy/HttpProxyHandler.java | 48 +++++++++--- .../handler/proxy/HttpProxyHandlerTest.java | 77 ++++++++++++++++++- 2 files changed, 114 insertions(+), 11 deletions(-) diff --git a/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java b/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java index 2c41faba0fff..c7c9519c4229 100644 --- a/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java +++ b/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java @@ -47,9 +47,10 @@ public final class HttpProxyHandler extends ProxyHandler { private final String username; private final String password; private final CharSequence authorization; + private final HttpHeaders outboundHeaders; private final boolean ignoreDefaultPortsInConnectHostHeader; private HttpResponseStatus status; - private HttpHeaders headers; + private HttpHeaders inboundHeaders; public HttpProxyHandler(SocketAddress proxyAddress) { this(proxyAddress, null); @@ -66,7 +67,7 @@ public HttpProxyHandler(SocketAddress proxyAddress, username = null; password = null; authorization = null; - this.headers = headers; + this.outboundHeaders = headers; this.ignoreDefaultPortsInConnectHostHeader = ignoreDefaultPortsInConnectHostHeader; } @@ -102,7 +103,7 @@ public HttpProxyHandler(SocketAddress proxyAddress, authz.release(); authzBase64.release(); - this.headers = headers; + this.outboundHeaders = headers; this.ignoreDefaultPortsInConnectHostHeader = ignoreDefaultPortsInConnectHostHeader; } @@ -163,32 +164,59 @@ protected Object newInitialMessage(ChannelHandlerContext ctx) throws Exception { req.headers().set(HttpHeaderNames.PROXY_AUTHORIZATION, authorization); } - if (headers != null) { - req.headers().add(headers); + if (outboundHeaders != null) { + req.headers().add(outboundHeaders); } return req; } @Override - protected boolean handleResponse(ChannelHandlerContext ctx, Object response) throws Exception { + protected boolean handleResponse(ChannelHandlerContext ctx, Object response) throws HttpProxyConnectException { if (response instanceof HttpResponse) { if (status != null) { - throw new ProxyConnectException(exceptionMessage("too many responses")); + throw new HttpProxyConnectException(exceptionMessage("too many responses"), /*headers=*/ null); } - status = ((HttpResponse) response).status(); + HttpResponse res = (HttpResponse) response; + status = res.status(); + inboundHeaders = res.headers(); } boolean finished = response instanceof LastHttpContent; if (finished) { if (status == null) { - throw new ProxyConnectException(exceptionMessage("missing response")); + throw new HttpProxyConnectException(exceptionMessage("missing response"), inboundHeaders); } if (status.code() != 200) { - throw new ProxyConnectException(exceptionMessage("status: " + status)); + throw new HttpProxyConnectException(exceptionMessage("status: " + status), inboundHeaders); } } return finished; } + + /** + * Specific case of a connection failure, which may include headers from the proxy. + */ + public static final class HttpProxyConnectException extends ProxyConnectException { + private static final long serialVersionUID = -8824334609292146066L; + + private final HttpHeaders headers; + + /** + * @param message The failure message. + * @param headers Header associated with the connection failure. May be {@code null}. + */ + public HttpProxyConnectException(String message, HttpHeaders headers) { + super(message); + this.headers = headers; + } + + /** + * Returns headers, if any. May be {@code null}. + */ + public HttpHeaders headers() { + return headers; + } + } } diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyHandlerTest.java b/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyHandlerTest.java index 04747e44c09f..5f920ab1f279 100644 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyHandlerTest.java +++ b/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyHandlerTest.java @@ -15,20 +15,36 @@ */ package io.netty.handler.proxy; +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPromise; +import io.netty.channel.DefaultEventLoopGroup; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.local.LocalChannel; +import io.netty.channel.local.LocalServerChannel; +import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpResponseEncoder; +import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.proxy.HttpProxyHandler.HttpProxyConnectException; import io.netty.util.NetUtil; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import java.net.InetAddress; import java.net.InetSocketAddress; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import static org.mockito.Mockito.*; public class HttpProxyHandlerTest { @@ -153,6 +169,65 @@ public void testCustomHeaders() throws Exception { true); } + @Test + public void testExceptionDuringConnect() throws Exception { + EventLoopGroup group = null; + Channel serverChannel = null; + Channel clientChannel = null; + try { + group = new DefaultEventLoopGroup(1); + final LocalAddress addr = new LocalAddress("a"); + final AtomicReference exception = new AtomicReference(); + ChannelFuture sf = + new ServerBootstrap().channel(LocalServerChannel.class).group(group).childHandler( + new ChannelInitializer() { + + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addFirst(new HttpResponseEncoder()); + DefaultFullHttpResponse response = new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, + HttpResponseStatus.BAD_GATEWAY); + response.headers().add("name", "value"); + response.headers().add(HttpHeaderNames.CONTENT_LENGTH, "0"); + ch.writeAndFlush(response); + } + }).bind(addr); + serverChannel = sf.sync().channel(); + ChannelFuture cf = new Bootstrap().channel(LocalChannel.class).group(group).handler( + new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addFirst(new HttpProxyHandler(addr)); + ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { + @Override + public void exceptionCaught(ChannelHandlerContext ctx, + Throwable cause) { + exception.set(cause); + } + }); + } + }).connect(new InetSocketAddress("localhost", 1234)); + clientChannel = cf.sync().channel(); + clientChannel.close().sync(); + + assertTrue(exception.get() instanceof HttpProxyConnectException); + HttpProxyConnectException actual = (HttpProxyConnectException) exception.get(); + assertNotNull(actual.headers()); + assertEquals("value", actual.headers().get("name")); + } finally { + if (clientChannel != null) { + clientChannel.close(); + } + if (serverChannel != null) { + serverChannel.close(); + } + if (group != null) { + group.shutdownGracefully(); + } + } + } + private static void testInitialMessage(InetSocketAddress socketAddress, String expectedUrl, String expectedHostHeader, From 32563bfcc129ef9332f175c277e4f6b59fd37d8c Mon Sep 17 00:00:00 2001 From: Roger Kapsi Date: Mon, 4 Feb 2019 03:57:54 -0500 Subject: [PATCH 371/417] Selective Message Aggregation (#8793) Motivation Implementations of MessageAggregator (HttpObjectAggregator in particular) may wish to selectively aggrerage requests and responses on a case-by-case basis such as for example only POST requests or only responses of a certain content-type. Modifications Adding a flag to MessageAggregator that toggles between true/false depending on if aggregation is desired for the current message or not. Result Fixes #8772 --- .../codec/http/HttpObjectAggregatorTest.java | 123 ++++++++++++++++++ .../handler/codec/MessageAggregator.java | 30 ++++- 2 files changed, 150 insertions(+), 3 deletions(-) diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java index 7dc0ac5a5195..503c93339f01 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java @@ -23,7 +23,10 @@ import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.DecoderResultProvider; import io.netty.handler.codec.TooLongFrameException; +import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; +import io.netty.util.ReferenceCountUtil; + import org.junit.Test; import org.mockito.Mockito; @@ -40,6 +43,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assert.assertSame; public class HttpObjectAggregatorTest { @@ -517,4 +521,123 @@ public void testReplaceAggregatedResponse() { aggregatedRep.release(); replacedRep.release(); } + + @Test + public void testSelectiveRequestAggregation() { + HttpObjectAggregator myPostAggregator = new HttpObjectAggregator(1024 * 1024) { + @Override + protected boolean isStartMessage(HttpObject msg) throws Exception { + if (msg instanceof HttpRequest) { + HttpRequest request = (HttpRequest) msg; + HttpMethod method = request.method(); + + if (method.equals(HttpMethod.POST)) { + return true; + } + } + + return false; + } + }; + + EmbeddedChannel channel = new EmbeddedChannel(myPostAggregator); + + try { + // Aggregate: POST + HttpRequest request1 = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); + HttpContent content1 = new DefaultHttpContent(Unpooled.copiedBuffer("Hello, World!", CharsetUtil.UTF_8)); + request1.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN); + + assertTrue(channel.writeInbound(request1, content1, LastHttpContent.EMPTY_LAST_CONTENT)); + + // Getting an aggregated response out + Object msg1 = channel.readInbound(); + try { + assertTrue(msg1 instanceof FullHttpRequest); + } finally { + ReferenceCountUtil.release(msg1); + } + + // Don't aggregate: non-POST + HttpRequest request2 = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "/"); + HttpContent content2 = new DefaultHttpContent(Unpooled.copiedBuffer("Hello, World!", CharsetUtil.UTF_8)); + request2.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN); + + try { + assertTrue(channel.writeInbound(request2, content2, LastHttpContent.EMPTY_LAST_CONTENT)); + + // Getting the same response objects out + assertSame(request2, channel.readInbound()); + assertSame(content2, channel.readInbound()); + assertSame(LastHttpContent.EMPTY_LAST_CONTENT, channel.readInbound()); + } finally { + ReferenceCountUtil.release(request2); + ReferenceCountUtil.release(content2); + } + + assertFalse(channel.finish()); + } finally { + channel.close(); + } + } + + @Test + public void testSelectiveResponseAggregation() { + HttpObjectAggregator myTextAggregator = new HttpObjectAggregator(1024 * 1024) { + @Override + protected boolean isStartMessage(HttpObject msg) throws Exception { + if (msg instanceof HttpResponse) { + HttpResponse response = (HttpResponse) msg; + HttpHeaders headers = response.headers(); + + String contentType = headers.get(HttpHeaderNames.CONTENT_TYPE); + if (AsciiString.contentEqualsIgnoreCase(contentType, HttpHeaderValues.TEXT_PLAIN)) { + return true; + } + } + + return false; + } + }; + + EmbeddedChannel channel = new EmbeddedChannel(myTextAggregator); + + try { + // Aggregate: text/plain + HttpResponse response1 = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); + HttpContent content1 = new DefaultHttpContent(Unpooled.copiedBuffer("Hello, World!", CharsetUtil.UTF_8)); + response1.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN); + + assertTrue(channel.writeInbound(response1, content1, LastHttpContent.EMPTY_LAST_CONTENT)); + + // Getting an aggregated response out + Object msg1 = channel.readInbound(); + try { + assertTrue(msg1 instanceof FullHttpResponse); + } finally { + ReferenceCountUtil.release(msg1); + } + + // Don't aggregate: application/json + HttpResponse response2 = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); + HttpContent content2 = new DefaultHttpContent(Unpooled.copiedBuffer("{key: 'value'}", CharsetUtil.UTF_8)); + response2.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON); + + try { + assertTrue(channel.writeInbound(response2, content2, LastHttpContent.EMPTY_LAST_CONTENT)); + + // Getting the same response objects out + assertSame(response2, channel.readInbound()); + assertSame(content2, channel.readInbound()); + assertSame(LastHttpContent.EMPTY_LAST_CONTENT, channel.readInbound()); + } finally { + ReferenceCountUtil.release(response2); + ReferenceCountUtil.release(content2); + } + + assertFalse(channel.finish()); + } finally { + channel.close(); + } + } } diff --git a/codec/src/main/java/io/netty/handler/codec/MessageAggregator.java b/codec/src/main/java/io/netty/handler/codec/MessageAggregator.java index 880756224b6b..11a75f2dd6e1 100644 --- a/codec/src/main/java/io/netty/handler/codec/MessageAggregator.java +++ b/codec/src/main/java/io/netty/handler/codec/MessageAggregator.java @@ -62,6 +62,8 @@ public abstract class MessageAggregator out) throws Exception { + assert aggregating; + if (isStartMessage(msg)) { handlingOversizedMessage = false; if (currentMessage != null) { @@ -245,7 +262,7 @@ public void operationComplete(ChannelFuture future) throws Exception { } else { aggregated = beginAggregation(m, EMPTY_BUFFER); } - finishAggregation(aggregated); + finishAggregation0(aggregated); out.add(aggregated); return; } @@ -300,7 +317,7 @@ public void operationComplete(ChannelFuture future) throws Exception { } if (last) { - finishAggregation(currentMessage); + finishAggregation0(currentMessage); // All done out.add(currentMessage); @@ -370,6 +387,11 @@ protected abstract Object newContinueResponse(S start, int maxContentLength, Cha */ protected void aggregate(O aggregated, C content) throws Exception { } + private void finishAggregation0(O aggregated) throws Exception { + aggregating = false; + finishAggregation(aggregated); + } + /** * Invoked when the specified {@code aggregated} message is about to be passed to the next handler in the pipeline. */ @@ -377,6 +399,7 @@ protected void finishAggregation(O aggregated) throws Exception { } private void invokeHandleOversizedMessage(ChannelHandlerContext ctx, S oversized) throws Exception { handlingOversizedMessage = true; + aggregating = false; currentMessage = null; try { handleOversizedMessage(ctx, oversized); @@ -440,6 +463,7 @@ private void releaseCurrentMessage() { currentMessage.release(); currentMessage = null; handlingOversizedMessage = false; + aggregating = false; } } } From b72fea340bf97e1ec012ed0c48fe1b930d12078e Mon Sep 17 00:00:00 2001 From: Dmitriy Dumanskiy Date: Mon, 4 Feb 2019 11:04:20 +0200 Subject: [PATCH 372/417] Improve DateFormatter parsing performance (#8821) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: Just was looking through code and found 1 interesting place DateFormatter.tryParseMonth that was not very effective, so I decided to optimize it a bit. Modification: Changed DateFormatter.tryParseMonth method. Instead of invocation regionMatch() for every month - compare chars one by one. Result: DateFormatter.parseHttpDate method performance improved from ~3% to ~15%. Benchmark (DATE_STRING) Mode Cnt Score Error Units DateFormatter2Benchmark.parseHttpHeaderDateFormatter Sun, 27 Jan 2016 19:18:46 GMT thrpt 6 4142781.221 ± 82155.002 ops/s DateFormatter2Benchmark.parseHttpHeaderDateFormatter Sun, 27 Dec 2016 19:18:46 GMT thrpt 6 3781810.558 ± 38679.061 ops/s DateFormatter2Benchmark.parseHttpHeaderDateFormatterNew Sun, 27 Jan 2016 19:18:46 GMT thrpt 6 4372569.705 ± 30257.537 ops/s DateFormatter2Benchmark.parseHttpHeaderDateFormatterNew Sun, 27 Dec 2016 19:18:46 GMT thrpt 6 4339785.100 ± 57542.660 ops/s --- .../io/netty/handler/codec/DateFormatter.java | 32 +++---- .../handler/codec/DateFormatterTest.java | 23 +++++ .../main/java/io/netty/util/AsciiString.java | 8 +- .../codec/DateFormatter2Benchmark.java | 95 +++++++++++++++++++ 4 files changed, 141 insertions(+), 17 deletions(-) create mode 100644 microbench/src/main/java/io/netty/handler/codec/DateFormatter2Benchmark.java diff --git a/codec/src/main/java/io/netty/handler/codec/DateFormatter.java b/codec/src/main/java/io/netty/handler/codec/DateFormatter.java index 86df148500fb..b07912abaa2e 100644 --- a/codec/src/main/java/io/netty/handler/codec/DateFormatter.java +++ b/codec/src/main/java/io/netty/handler/codec/DateFormatter.java @@ -261,10 +261,6 @@ private boolean tryParseDayOfMonth(CharSequence txt, int tokenStart, int tokenEn return false; } - private static boolean matchMonth(String month, CharSequence txt, int tokenStart) { - return AsciiString.regionMatchesAscii(month, true, 0, txt, tokenStart, 3); - } - private boolean tryParseMonth(CharSequence txt, int tokenStart, int tokenEnd) { int len = tokenEnd - tokenStart; @@ -272,29 +268,33 @@ private boolean tryParseMonth(CharSequence txt, int tokenStart, int tokenEnd) { return false; } - if (matchMonth("Jan", txt, tokenStart)) { + char monthChar1 = AsciiString.toLowerCase(txt.charAt(tokenStart)); + char monthChar2 = AsciiString.toLowerCase(txt.charAt(tokenStart + 1)); + char monthChar3 = AsciiString.toLowerCase(txt.charAt(tokenStart + 2)); + + if (monthChar1 == 'j' && monthChar2 == 'a' && monthChar3 == 'n') { month = Calendar.JANUARY; - } else if (matchMonth("Feb", txt, tokenStart)) { + } else if (monthChar1 == 'f' && monthChar2 == 'e' && monthChar3 == 'b') { month = Calendar.FEBRUARY; - } else if (matchMonth("Mar", txt, tokenStart)) { + } else if (monthChar1 == 'm' && monthChar2 == 'a' && monthChar3 == 'r') { month = Calendar.MARCH; - } else if (matchMonth("Apr", txt, tokenStart)) { + } else if (monthChar1 == 'a' && monthChar2 == 'p' && monthChar3 == 'r') { month = Calendar.APRIL; - } else if (matchMonth("May", txt, tokenStart)) { + } else if (monthChar1 == 'm' && monthChar2 == 'a' && monthChar3 == 'y') { month = Calendar.MAY; - } else if (matchMonth("Jun", txt, tokenStart)) { + } else if (monthChar1 == 'j' && monthChar2 == 'u' && monthChar3 == 'n') { month = Calendar.JUNE; - } else if (matchMonth("Jul", txt, tokenStart)) { + } else if (monthChar1 == 'j' && monthChar2 == 'u' && monthChar3 == 'l') { month = Calendar.JULY; - } else if (matchMonth("Aug", txt, tokenStart)) { + } else if (monthChar1 == 'a' && monthChar2 == 'u' && monthChar3 == 'g') { month = Calendar.AUGUST; - } else if (matchMonth("Sep", txt, tokenStart)) { + } else if (monthChar1 == 's' && monthChar2 == 'e' && monthChar3 == 'p') { month = Calendar.SEPTEMBER; - } else if (matchMonth("Oct", txt, tokenStart)) { + } else if (monthChar1 == 'o' && monthChar2 == 'c' && monthChar3 == 't') { month = Calendar.OCTOBER; - } else if (matchMonth("Nov", txt, tokenStart)) { + } else if (monthChar1 == 'n' && monthChar2 == 'o' && monthChar3 == 'v') { month = Calendar.NOVEMBER; - } else if (matchMonth("Dec", txt, tokenStart)) { + } else if (monthChar1 == 'd' && monthChar2 == 'e' && monthChar3 == 'c') { month = Calendar.DECEMBER; } else { return false; diff --git a/codec/src/test/java/io/netty/handler/codec/DateFormatterTest.java b/codec/src/test/java/io/netty/handler/codec/DateFormatterTest.java index 99d1d3864a9a..e833fdfb605c 100644 --- a/codec/src/test/java/io/netty/handler/codec/DateFormatterTest.java +++ b/codec/src/test/java/io/netty/handler/codec/DateFormatterTest.java @@ -17,6 +17,7 @@ import org.junit.Test; +import java.util.Calendar; import java.util.Date; import static org.junit.Assert.*; @@ -111,4 +112,26 @@ public void testParseInvalidInput() { public void testFormat() { assertEquals("Sun, 6 Nov 1994 08:49:37 GMT", format(DATE)); } + + @Test + public void testParseAllMonths() { + assertEquals(Calendar.JANUARY, getMonth(parseHttpDate("Sun, 6 Jan 1994 08:49:37 GMT"))); + assertEquals(Calendar.FEBRUARY, getMonth(parseHttpDate("Sun, 6 Feb 1994 08:49:37 GMT"))); + assertEquals(Calendar.MARCH, getMonth(parseHttpDate("Sun, 6 Mar 1994 08:49:37 GMT"))); + assertEquals(Calendar.APRIL, getMonth(parseHttpDate("Sun, 6 Apr 1994 08:49:37 GMT"))); + assertEquals(Calendar.MAY, getMonth(parseHttpDate("Sun, 6 May 1994 08:49:37 GMT"))); + assertEquals(Calendar.JUNE, getMonth(parseHttpDate("Sun, 6 Jun 1994 08:49:37 GMT"))); + assertEquals(Calendar.JULY, getMonth(parseHttpDate("Sun, 6 Jul 1994 08:49:37 GMT"))); + assertEquals(Calendar.AUGUST, getMonth(parseHttpDate("Sun, 6 Aug 1994 08:49:37 GMT"))); + assertEquals(Calendar.SEPTEMBER, getMonth(parseHttpDate("Sun, 6 Sep 1994 08:49:37 GMT"))); + assertEquals(Calendar.OCTOBER, getMonth(parseHttpDate("Sun Oct 6 08:49:37 1994"))); + assertEquals(Calendar.NOVEMBER, getMonth(parseHttpDate("Sun Nov 6 08:49:37 1994"))); + assertEquals(Calendar.DECEMBER, getMonth(parseHttpDate("Sun Dec 6 08:49:37 1994"))); + } + + private static int getMonth(Date referenceDate) { + Calendar cal = Calendar.getInstance(); + cal.setTime(referenceDate); + return cal.get(Calendar.MONTH); + } } diff --git a/common/src/main/java/io/netty/util/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java index f53746960a89..302b36e89bc6 100644 --- a/common/src/main/java/io/netty/util/AsciiString.java +++ b/common/src/main/java/io/netty/util/AsciiString.java @@ -1827,7 +1827,13 @@ private static byte toLowerCase(byte b) { return isUpperCase(b) ? (byte) (b + 32) : b; } - private static char toLowerCase(char c) { + /** + * If the character is uppercase - converts the character to lowercase, + * otherwise returns the character as it is. Only for ASCII characters. + * + * @return lowercase ASCII character equivalent + */ + public static char toLowerCase(char c) { return isUpperCase(c) ? (char) (c + 32) : c; } diff --git a/microbench/src/main/java/io/netty/handler/codec/DateFormatter2Benchmark.java b/microbench/src/main/java/io/netty/handler/codec/DateFormatter2Benchmark.java new file mode 100644 index 000000000000..dd44ed60cc26 --- /dev/null +++ b/microbench/src/main/java/io/netty/handler/codec/DateFormatter2Benchmark.java @@ -0,0 +1,95 @@ +/* + * Copyright 2019 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec; + +import io.netty.microbench.util.AbstractMicrobenchmark; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.Date; + +@Threads(1) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +public class DateFormatter2Benchmark extends AbstractMicrobenchmark { + + @Param({"Sun, 27 Jan 2016 19:18:46 GMT", "Sun, 27 Dec 2016 19:18:46 GMT"}) + String DATE_STRING; + + @Benchmark + public Date parseHttpHeaderDateFormatterNew() { + return DateFormatter.parseHttpDate(DATE_STRING); + } + + /* + @Benchmark + public Date parseHttpHeaderDateFormatter() { + return DateFormatterOld.parseHttpDate(DATE_STRING); + } + */ + + /* + * Benchmark (DATE_STRING) Mode Cnt Score Error Units + * parseHttpHeaderDateFormatter Sun, 27 Jan 2016 19:18:46 GMT thrpt 6 4142781.221 ± 82155.002 ops/s + * parseHttpHeaderDateFormatter Sun, 27 Dec 2016 19:18:46 GMT thrpt 6 3781810.558 ± 38679.061 ops/s + * parseHttpHeaderDateFormatterNew Sun, 27 Jan 2016 19:18:46 GMT thrpt 6 4372569.705 ± 30257.537 ops/s + * parseHttpHeaderDateFormatterNew Sun, 27 Dec 2016 19:18:46 GMT thrpt 6 4339785.100 ± 57542.660 ops/s + */ + + /*Old DateFormatter.tryParseMonth method: + private boolean tryParseMonth(CharSequence txt, int tokenStart, int tokenEnd) { + int len = tokenEnd - tokenStart; + + if (len != 3) { + return false; + } + + if (matchMonth("Jan", txt, tokenStart)) { + month = Calendar.JANUARY; + } else if (matchMonth("Feb", txt, tokenStart)) { + month = Calendar.FEBRUARY; + } else if (matchMonth("Mar", txt, tokenStart)) { + month = Calendar.MARCH; + } else if (matchMonth("Apr", txt, tokenStart)) { + month = Calendar.APRIL; + } else if (matchMonth("May", txt, tokenStart)) { + month = Calendar.MAY; + } else if (matchMonth("Jun", txt, tokenStart)) { + month = Calendar.JUNE; + } else if (matchMonth("Jul", txt, tokenStart)) { + month = Calendar.JULY; + } else if (matchMonth("Aug", txt, tokenStart)) { + month = Calendar.AUGUST; + } else if (matchMonth("Sep", txt, tokenStart)) { + month = Calendar.SEPTEMBER; + } else if (matchMonth("Oct", txt, tokenStart)) { + month = Calendar.OCTOBER; + } else if (matchMonth("Nov", txt, tokenStart)) { + month = Calendar.NOVEMBER; + } else if (matchMonth("Dec", txt, tokenStart)) { + month = Calendar.DECEMBER; + } else { + return false; + } + + return true; + } + */ + +} From 4c64c98f348131e0792ba4a92ce3d0003237d56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E6=AC=A7?= Date: Mon, 4 Feb 2019 22:55:07 +0800 Subject: [PATCH 373/417] use checkPositive/checkPositiveOrZero (#8835) Motivation: We can replace some "hand-rolled" integer checks with our own static utility method to simplify the code. Modifications: Use methods provided by `ObjectUtil`. Result: Cleaner code and less duplication --- .../handler/codec/dns/AbstractDnsRecord.java | 5 ++--- .../codec/http/DefaultHttpHeaders.java | 3 +-- .../handler/codec/http/HttpObjectDecoder.java | 21 ++++++------------- .../codec/http/HttpResponseStatus.java | 6 ++---- .../netty/handler/codec/http/HttpVersion.java | 10 ++++----- .../multipart/AbstractMemoryHttpData.java | 3 +-- .../codec/spdy/DefaultSpdyGoAwayFrame.java | 7 +++---- .../codec/spdy/DefaultSpdyStreamFrame.java | 7 +++---- .../codec/spdy/DefaultSpdySynReplyFrame.java | 3 +-- .../codec/spdy/DefaultSpdySynStreamFrame.java | 8 +++---- .../spdy/DefaultSpdyWindowUpdateFrame.java | 14 +++++-------- .../handler/codec/spdy/SpdyFrameDecoder.java | 7 +++---- .../handler/codec/spdy/SpdyHttpDecoder.java | 6 ++---- .../codec/spdy/SpdySessionHandler.java | 19 ++++++++--------- .../http2/DefaultHttp2ConnectionEncoder.java | 5 ++--- .../codec/http2/DefaultHttp2FrameWriter.java | 14 +++++-------- .../codec/http2/DefaultHttp2GoAwayFrame.java | 6 +++--- .../DefaultHttp2LocalFlowController.java | 5 ++--- .../DefaultHttp2RemoteFlowController.java | 5 ++--- .../DelegatingDecompressorFrameListener.java | 5 ++--- .../http2/UniformStreamByteDistributor.java | 5 ++--- .../WeightedFairQueueByteDistributor.java | 11 +++++----- .../binary/AbstractBinaryMemcacheDecoder.java | 6 +++--- .../codec/stomp/StompSubframeDecoder.java | 13 +++--------- 24 files changed, 74 insertions(+), 120 deletions(-) diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsRecord.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsRecord.java index 28b92c27f928..2ba6e573a7fd 100644 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsRecord.java +++ b/codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsRecord.java @@ -21,6 +21,7 @@ import java.net.IDN; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; /** * A skeletal implementation of {@link DnsRecord}. @@ -62,9 +63,7 @@ protected AbstractDnsRecord(String name, DnsRecordType type, long timeToLive) { * @param timeToLive the TTL value of the record */ protected AbstractDnsRecord(String name, DnsRecordType type, int dnsClass, long timeToLive) { - if (timeToLive < 0) { - throw new IllegalArgumentException("timeToLive: " + timeToLive + " (expected: >= 0)"); - } + checkPositiveOrZero(timeToLive, "timeToLive"); // Convert to ASCII which will also check that the length is not too big. // See: // - https://github.com/netty/netty/issues/4937 diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java index 88af27f738e6..ef24c7558342 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java @@ -372,8 +372,7 @@ private static void validateHeaderNameElement(byte value) { default: // Check to see if the character is not an ASCII character, or invalid if (value < 0) { - throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " + - value); + throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " + value); } } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index f8220cdb04eb..6d1d596f781c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec.http; +import static io.netty.util.internal.ObjectUtil.checkPositive; + import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; @@ -168,21 +170,10 @@ protected HttpObjectDecoder( protected HttpObjectDecoder( int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported, boolean validateHeaders, int initialBufferSize) { - if (maxInitialLineLength <= 0) { - throw new IllegalArgumentException( - "maxInitialLineLength must be a positive integer: " + - maxInitialLineLength); - } - if (maxHeaderSize <= 0) { - throw new IllegalArgumentException( - "maxHeaderSize must be a positive integer: " + - maxHeaderSize); - } - if (maxChunkSize <= 0) { - throw new IllegalArgumentException( - "maxChunkSize must be a positive integer: " + - maxChunkSize); - } + checkPositive(maxInitialLineLength, "maxInitialLineLength"); + checkPositive(maxHeaderSize, "maxHeaderSize"); + checkPositive(maxChunkSize, "maxChunkSize"); + AppendableCharSequence seq = new AppendableCharSequence(initialBufferSize); lineParser = new LineParser(seq, maxInitialLineLength); headerParser = new HeaderParser(seq, maxHeaderSize); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java index b7e2c10d456c..ef60a41d4f3b 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java @@ -22,6 +22,7 @@ import static io.netty.handler.codec.http.HttpConstants.SP; import static io.netty.util.ByteProcessor.FIND_ASCII_SPACE; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; import static java.lang.Integer.parseInt; /** @@ -538,10 +539,7 @@ public HttpResponseStatus(int code, String reasonPhrase) { } private HttpResponseStatus(int code, String reasonPhrase, boolean bytes) { - if (code < 0) { - throw new IllegalArgumentException( - "code: " + code + " (expected: 0+)"); - } + checkPositiveOrZero(code, "code"); if (reasonPhrase == null) { throw new NullPointerException("reasonPhrase"); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java index a643f42458d9..7ba40eed90c3 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec.http; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.buffer.ByteBuf; import io.netty.util.CharsetUtil; @@ -165,12 +167,8 @@ private HttpVersion( } } - if (majorVersion < 0) { - throw new IllegalArgumentException("negative majorVersion"); - } - if (minorVersion < 0) { - throw new IllegalArgumentException("negative minorVersion"); - } + checkPositiveOrZero(majorVersion, "majorVersion"); + checkPositiveOrZero(minorVersion, "minorVersion"); this.protocolName = protocolName; this.majorVersion = majorVersion; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpData.java index 31aa9ce64b5f..4cb7e567b252 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpData.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpData.java @@ -128,8 +128,7 @@ public void setContent(File file) throws IOException { } long newsize = file.length(); if (newsize > Integer.MAX_VALUE) { - throw new IllegalArgumentException( - "File too big to be loaded in memory"); + throw new IllegalArgumentException("File too big to be loaded in memory"); } checkSize(newsize); FileInputStream inputStream = new FileInputStream(file); diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyGoAwayFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyGoAwayFrame.java index 4d88875a6e8c..79c21f2404d2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyGoAwayFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyGoAwayFrame.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec.spdy; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.util.internal.StringUtil; /** @@ -62,10 +64,7 @@ public int lastGoodStreamId() { @Override public SpdyGoAwayFrame setLastGoodStreamId(int lastGoodStreamId) { - if (lastGoodStreamId < 0) { - throw new IllegalArgumentException("Last-good-stream-ID" - + " cannot be negative: " + lastGoodStreamId); - } + checkPositiveOrZero(lastGoodStreamId, "lastGoodStreamId"); this.lastGoodStreamId = lastGoodStreamId; return this; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyStreamFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyStreamFrame.java index 4618d4d4a95c..487844ecd913 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyStreamFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyStreamFrame.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec.spdy; +import static io.netty.util.internal.ObjectUtil.checkPositive; + /** * The default {@link SpdyStreamFrame} implementation. */ @@ -39,10 +41,7 @@ public int streamId() { @Override public SpdyStreamFrame setStreamId(int streamId) { - if (streamId <= 0) { - throw new IllegalArgumentException( - "Stream-ID must be positive: " + streamId); - } + checkPositive(streamId, "streamId"); this.streamId = streamId; return this; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynReplyFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynReplyFrame.java index 7efc905641e3..f757d1dbd663 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynReplyFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynReplyFrame.java @@ -20,8 +20,7 @@ /** * The default {@link SpdySynReplyFrame} implementation. */ -public class DefaultSpdySynReplyFrame extends DefaultSpdyHeadersFrame - implements SpdySynReplyFrame { +public class DefaultSpdySynReplyFrame extends DefaultSpdyHeadersFrame implements SpdySynReplyFrame { /** * Creates a new instance. diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynStreamFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynStreamFrame.java index f8adc1c5f1ca..46fe30163634 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynStreamFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynStreamFrame.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec.spdy; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.util.internal.StringUtil; /** @@ -77,11 +79,7 @@ public int associatedStreamId() { @Override public SpdySynStreamFrame setAssociatedStreamId(int associatedStreamId) { - if (associatedStreamId < 0) { - throw new IllegalArgumentException( - "Associated-To-Stream-ID cannot be negative: " + - associatedStreamId); - } + checkPositiveOrZero(associatedStreamId, "associatedStreamId"); this.associatedStreamId = associatedStreamId; return this; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyWindowUpdateFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyWindowUpdateFrame.java index f14611bac614..22b0406c80c0 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyWindowUpdateFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyWindowUpdateFrame.java @@ -15,6 +15,9 @@ */ package io.netty.handler.codec.spdy; +import static io.netty.util.internal.ObjectUtil.checkPositive; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.util.internal.StringUtil; /** @@ -43,10 +46,7 @@ public int streamId() { @Override public SpdyWindowUpdateFrame setStreamId(int streamId) { - if (streamId < 0) { - throw new IllegalArgumentException( - "Stream-ID cannot be negative: " + streamId); - } + checkPositiveOrZero(streamId, "streamId"); this.streamId = streamId; return this; } @@ -58,11 +58,7 @@ public int deltaWindowSize() { @Override public SpdyWindowUpdateFrame setDeltaWindowSize(int deltaWindowSize) { - if (deltaWindowSize <= 0) { - throw new IllegalArgumentException( - "Delta-Window-Size must be positive: " + - deltaWindowSize); - } + checkPositive(deltaWindowSize, "deltaWindowSize"); this.deltaWindowSize = deltaWindowSize; return this; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java index e0d1112813b7..fc432b683096 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java @@ -38,6 +38,8 @@ import static io.netty.handler.codec.spdy.SpdyCodecUtil.getUnsignedInt; import static io.netty.handler.codec.spdy.SpdyCodecUtil.getUnsignedMedium; import static io.netty.handler.codec.spdy.SpdyCodecUtil.getUnsignedShort; +import static io.netty.util.internal.ObjectUtil.checkPositive; + import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -95,10 +97,7 @@ public SpdyFrameDecoder(SpdyVersion spdyVersion, SpdyFrameDecoderDelegate delega if (delegate == null) { throw new NullPointerException("delegate"); } - if (maxChunkSize <= 0) { - throw new IllegalArgumentException( - "maxChunkSize must be a positive integer: " + maxChunkSize); - } + checkPositive(maxChunkSize, "maxChunkSize"); this.spdyVersion = spdyVersion.getVersion(); this.delegate = delegate; this.maxChunkSize = maxChunkSize; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java index 366ad15b662f..5e16a6f4f2a7 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java @@ -38,6 +38,7 @@ import java.util.Map; import static io.netty.handler.codec.spdy.SpdyHeaders.HttpNames.*; +import static io.netty.util.internal.ObjectUtil.checkPositive; /** * Decodes {@link SpdySynStreamFrame}s, {@link SpdySynReplyFrame}s, @@ -103,10 +104,7 @@ protected SpdyHttpDecoder(SpdyVersion version, int maxContentLength, Map= 0"); - } + checkPositiveOrZero(padding, "padding"); this.padding = padding; this.endOfStream = endOfStream; this.stream = stream; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java index 1e4cdcd93704..e2b23963378a 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java @@ -61,6 +61,8 @@ import static io.netty.handler.codec.http2.Http2FrameTypes.SETTINGS; import static io.netty.handler.codec.http2.Http2FrameTypes.WINDOW_UPDATE; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.ObjectUtil.checkPositive; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; import static java.lang.Math.max; import static java.lang.Math.min; @@ -614,15 +616,11 @@ private static void writePaddingLength(ByteBuf buf, int padding) { } private static void verifyStreamId(int streamId, String argumentName) { - if (streamId <= 0) { - throw new IllegalArgumentException(argumentName + " must be > 0"); - } + checkPositive(streamId, "streamId"); } private static void verifyStreamOrConnectionId(int streamId, String argumentName) { - if (streamId < 0) { - throw new IllegalArgumentException(argumentName + " must be >= 0"); - } + checkPositiveOrZero(streamId, "streamId"); } private static void verifyWeight(short weight) { @@ -638,9 +636,7 @@ private static void verifyErrorCode(long errorCode) { } private static void verifyWindowSizeIncrement(int windowSizeIncrement) { - if (windowSizeIncrement < 0) { - throw new IllegalArgumentException("WindowSizeIncrement must be >= 0"); - } + checkPositiveOrZero(windowSizeIncrement, "windowSizeIncrement"); } private static void verifyPingPayload(ByteBuf data) { diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2GoAwayFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2GoAwayFrame.java index 77207673303f..2dbd738d04bd 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2GoAwayFrame.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2GoAwayFrame.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec.http2; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.buffer.ByteBuf; import io.netty.buffer.DefaultByteBufHolder; import io.netty.buffer.Unpooled; @@ -98,9 +100,7 @@ public int extraStreamIds() { @Override public Http2GoAwayFrame setExtraStreamIds(int extraStreamIds) { - if (extraStreamIds < 0) { - throw new IllegalArgumentException("extraStreamIds must be non-negative"); - } + checkPositiveOrZero(extraStreamIds, "extraStreamIds"); this.extraStreamIds = extraStreamIds; return this; } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java index 9c8308ecf3d3..3dccea5056e4 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java @@ -24,6 +24,7 @@ import static io.netty.handler.codec.http2.Http2Exception.connectionError; import static io.netty.handler.codec.http2.Http2Exception.streamError; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; import static java.lang.Math.max; import static java.lang.Math.min; import io.netty.buffer.ByteBuf; @@ -173,9 +174,7 @@ public void incrementWindowSize(Http2Stream stream, int delta) throws Http2Excep @Override public boolean consumeBytes(Http2Stream stream, int numBytes) throws Http2Exception { assert ctx != null && ctx.executor().inEventLoop(); - if (numBytes < 0) { - throw new IllegalArgumentException("numBytes must not be negative"); - } + checkPositiveOrZero(numBytes, "numBytes"); if (numBytes == 0) { return false; } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowController.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowController.java index 217cf8dc251d..ef6ec986ee8f 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowController.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowController.java @@ -31,6 +31,7 @@ import static io.netty.handler.codec.http2.Http2Exception.streamError; import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_LOCAL; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; import static java.lang.Math.max; import static java.lang.Math.min; @@ -635,9 +636,7 @@ final void writePendingBytes() throws Http2Exception { } void initialWindowSize(int newWindowSize) throws Http2Exception { - if (newWindowSize < 0) { - throw new IllegalArgumentException("Invalid initial window size: " + newWindowSize); - } + checkPositiveOrZero(newWindowSize, "newWindowSize"); final int delta = newWindowSize - initialWindowSize; initialWindowSize = newWindowSize; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java index 78ef230c62ff..3e73bd68dd86 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java @@ -33,6 +33,7 @@ import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; import static io.netty.handler.codec.http2.Http2Exception.streamError; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; /** * A HTTP2 frame listener that will decompress data frames according to the {@code content-encoding} header for each @@ -398,9 +399,7 @@ void incrementDecompressedBytes(int delta) { * @return The number of pre-decompressed bytes that have been consumed. */ int consumeBytes(int streamId, int decompressedBytes) throws Http2Exception { - if (decompressedBytes < 0) { - throw new IllegalArgumentException("decompressedBytes must not be negative: " + decompressedBytes); - } + checkPositiveOrZero(decompressedBytes, "decompressedBytes"); if (decompressed - decompressedBytes < 0) { throw streamError(streamId, INTERNAL_ERROR, "Attempting to return too many bytes for stream %d. decompressed: %d " + diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/UniformStreamByteDistributor.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/UniformStreamByteDistributor.java index 421d3488b20c..c7e57911b8f2 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/UniformStreamByteDistributor.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/UniformStreamByteDistributor.java @@ -24,6 +24,7 @@ import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; import static io.netty.handler.codec.http2.Http2Exception.connectionError; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.ObjectUtil.checkPositive; import static java.lang.Math.max; import static java.lang.Math.min; @@ -72,9 +73,7 @@ public void onStreamClosed(Http2Stream stream) { * Must be > 0. */ public void minAllocationChunk(int minAllocationChunk) { - if (minAllocationChunk <= 0) { - throw new IllegalArgumentException("minAllocationChunk must be > 0"); - } + checkPositive(minAllocationChunk, "minAllocationChunk"); this.minAllocationChunk = minAllocationChunk; } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/WeightedFairQueueByteDistributor.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/WeightedFairQueueByteDistributor.java index b7c2cddf52cd..b0f3a25dc4a4 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/WeightedFairQueueByteDistributor.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/WeightedFairQueueByteDistributor.java @@ -37,6 +37,8 @@ import static io.netty.handler.codec.http2.Http2CodecUtil.streamableBytes; import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; import static io.netty.handler.codec.http2.Http2Exception.connectionError; +import static io.netty.util.internal.ObjectUtil.checkPositive; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; import static java.lang.Integer.MAX_VALUE; import static java.lang.Math.max; import static java.lang.Math.min; @@ -96,9 +98,8 @@ public WeightedFairQueueByteDistributor(Http2Connection connection) { } public WeightedFairQueueByteDistributor(Http2Connection connection, int maxStateOnlySize) { - if (maxStateOnlySize < 0) { - throw new IllegalArgumentException("maxStateOnlySize: " + maxStateOnlySize + " (expected: >0)"); - } else if (maxStateOnlySize == 0) { + checkPositiveOrZero(maxStateOnlySize, "maxStateOnlySize"); + if (maxStateOnlySize == 0) { stateOnlyMap = IntCollections.emptyMap(); stateOnlyRemovalQueue = EmptyPriorityQueue.instance(); } else { @@ -281,9 +282,7 @@ public boolean distribute(int maxBytes, Writer writer) throws Http2Exception { * @param allocationQuantum the amount of bytes that will be allocated to each stream. Must be > 0. */ public void allocationQuantum(int allocationQuantum) { - if (allocationQuantum <= 0) { - throw new IllegalArgumentException("allocationQuantum must be > 0"); - } + checkPositive(allocationQuantum, "allocationQuantum"); this.allocationQuantum = allocationQuantum; } diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/AbstractBinaryMemcacheDecoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/AbstractBinaryMemcacheDecoder.java index 2c9038282991..bec754afbda2 100644 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/AbstractBinaryMemcacheDecoder.java +++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/AbstractBinaryMemcacheDecoder.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec.memcache.binary; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; + import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; @@ -59,9 +61,7 @@ protected AbstractBinaryMemcacheDecoder() { * @param chunkSize the maximum chunk size of the payload. */ protected AbstractBinaryMemcacheDecoder(int chunkSize) { - if (chunkSize < 0) { - throw new IllegalArgumentException("chunkSize must be a positive integer: " + chunkSize); - } + checkPositiveOrZero(chunkSize, "chunkSize"); this.chunkSize = chunkSize; } diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java index b4d6a1495823..0139426172f4 100644 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java +++ b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java @@ -30,6 +30,7 @@ import static io.netty.buffer.ByteBufUtil.indexOf; import static io.netty.buffer.ByteBufUtil.readBytes; +import static io.netty.util.internal.ObjectUtil.checkPositive; /** * Decodes {@link ByteBuf}s into {@link StompHeadersSubframe}s and @@ -90,16 +91,8 @@ public StompSubframeDecoder(int maxLineLength, int maxChunkSize) { public StompSubframeDecoder(int maxLineLength, int maxChunkSize, boolean validateHeaders) { super(State.SKIP_CONTROL_CHARACTERS); - if (maxLineLength <= 0) { - throw new IllegalArgumentException( - "maxLineLength must be a positive integer: " + - maxLineLength); - } - if (maxChunkSize <= 0) { - throw new IllegalArgumentException( - "maxChunkSize must be a positive integer: " + - maxChunkSize); - } + checkPositive(maxLineLength, "maxLineLength"); + checkPositive(maxChunkSize, "maxChunkSize"); this.maxChunkSize = maxChunkSize; this.maxLineLength = maxLineLength; this.validateHeaders = validateHeaders; From 737519314153f9146eaf532cd4e945a621cf3fa5 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 4 Feb 2019 19:07:42 +0100 Subject: [PATCH 374/417] Don't update state of PromiseCombiner when finish(null) is called (#8843) Motivation: When we fail a call to PromiseCombiner.finish(...) because of a null argument we must not update the internal state before throwing. Modifications: - First do the null check and only after we validated that the argument is not null update the internal state - Add test case. Modifications: Do not mess up internal state of PromiseCombiner when finish(...) is called with a null argument. Result: After your change, what will change. --- .../io/netty/util/concurrent/PromiseCombiner.java | 3 ++- .../netty/util/concurrent/PromiseCombinerTest.java | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/io/netty/util/concurrent/PromiseCombiner.java b/common/src/main/java/io/netty/util/concurrent/PromiseCombiner.java index 6624f05db1ef..38b9de14cbc2 100644 --- a/common/src/main/java/io/netty/util/concurrent/PromiseCombiner.java +++ b/common/src/main/java/io/netty/util/concurrent/PromiseCombiner.java @@ -112,11 +112,12 @@ public void addAll(Future... futures) { * @param aggregatePromise the promise to notify when all combined futures have finished */ public void finish(Promise aggregatePromise) { + ObjectUtil.checkNotNull(aggregatePromise, "aggregatePromise"); if (doneAdding) { throw new IllegalStateException("Already finished"); } doneAdding = true; - this.aggregatePromise = ObjectUtil.checkNotNull(aggregatePromise, "aggregatePromise"); + this.aggregatePromise = aggregatePromise; if (doneCount == expectedCount) { tryPromise(); } diff --git a/common/src/test/java/io/netty/util/concurrent/PromiseCombinerTest.java b/common/src/test/java/io/netty/util/concurrent/PromiseCombinerTest.java index b46fa4102497..d77aab70ce49 100644 --- a/common/src/test/java/io/netty/util/concurrent/PromiseCombinerTest.java +++ b/common/src/test/java/io/netty/util/concurrent/PromiseCombinerTest.java @@ -15,6 +15,7 @@ */ package io.netty.util.concurrent; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -58,6 +59,18 @@ public void setup() { combiner = new PromiseCombiner(); } + @Test + public void testNullArgument() { + try { + combiner.finish(null); + Assert.fail(); + } catch (NullPointerException expected) { + // expected + } + combiner.finish(p1); + verify(p1).trySuccess(null); + } + @Test public void testNullAggregatePromise() { combiner.finish(p1); From c6a90d90a6558a78dee53e4a9667addde870e849 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 8 Feb 2019 20:08:34 +0100 Subject: [PATCH 375/417] Add more tests to KQueue and Epoll testsuites. (#8851) Motivation: We missed to extend a few tests from the testsuite and so also run these with our native KQueue and Epoll transport. Modifications: Extend tests and so run these for our native transports as well. Result: More tests. --- ...lDomainSocketSslClientRenegotiateTest.java | 42 +++++++++++++++++++ .../EpollSocketSslClientRenegotiateTest.java | 36 ++++++++++++++++ .../epoll/EpollSocketSslSessionReuseTest.java | 36 ++++++++++++++++ .../epoll/EpollWriteBeforeRegisteredTest.java | 30 +++++++++++++ ...eDomainSocketSslClientRenegotiateTest.java | 42 +++++++++++++++++++ .../KQueueSocketSslClientRenegotiateTest.java | 36 ++++++++++++++++ .../KQueueSocketSslSessionReuseTest.java | 36 ++++++++++++++++ .../KqueueWriteBeforeRegisteredTest.java | 30 +++++++++++++ 8 files changed, 288 insertions(+) create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslClientRenegotiateTest.java create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslClientRenegotiateTest.java create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslSessionReuseTest.java create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollWriteBeforeRegisteredTest.java create mode 100644 transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslClientRenegotiateTest.java create mode 100644 transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslClientRenegotiateTest.java create mode 100644 transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslSessionReuseTest.java create mode 100644 transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KqueueWriteBeforeRegisteredTest.java diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslClientRenegotiateTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslClientRenegotiateTest.java new file mode 100644 index 000000000000..9037c18adf6f --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslClientRenegotiateTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 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: + * + * http://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.channel.epoll; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.handler.ssl.SslContext; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.SocketSslClientRenegotiateTest; + +import java.net.SocketAddress; +import java.util.List; + +public class EpollDomainSocketSslClientRenegotiateTest extends SocketSslClientRenegotiateTest { + + public EpollDomainSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx) { + super(serverCtx, clientCtx); + } + + @Override + protected List> newFactories() { + return EpollSocketTestPermutation.INSTANCE.domainSocket(); + } + + @Override + protected SocketAddress newSocketAddress() { + return EpollSocketTestPermutation.newSocketAddress(); + } +} diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslClientRenegotiateTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslClientRenegotiateTest.java new file mode 100644 index 000000000000..abe74d2ff5a4 --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslClientRenegotiateTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019 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: + * + * http://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.channel.epoll; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.handler.ssl.SslContext; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.SocketSslClientRenegotiateTest; + +import java.util.List; + +public class EpollSocketSslClientRenegotiateTest extends SocketSslClientRenegotiateTest { + + public EpollSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx) { + super(serverCtx, clientCtx); + } + + @Override + protected List> newFactories() { + return EpollSocketTestPermutation.INSTANCE.socket(); + } +} diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslSessionReuseTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslSessionReuseTest.java new file mode 100644 index 000000000000..2b782247c8fe --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslSessionReuseTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019 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: + * + * http://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.channel.epoll; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.handler.ssl.SslContext; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.SocketSslSessionReuseTest; + +import java.util.List; + +public class EpollSocketSslSessionReuseTest extends SocketSslSessionReuseTest { + + public EpollSocketSslSessionReuseTest(SslContext serverCtx, SslContext clientCtx) { + super(serverCtx, clientCtx); + } + + @Override + protected List> newFactories() { + return EpollSocketTestPermutation.INSTANCE.socket(); + } +} diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollWriteBeforeRegisteredTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollWriteBeforeRegisteredTest.java new file mode 100644 index 000000000000..b1943fd2f804 --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollWriteBeforeRegisteredTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019 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: + * + * http://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.channel.epoll; + +import io.netty.bootstrap.Bootstrap; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.WriteBeforeRegisteredTest; + +import java.util.List; + +public class EpollWriteBeforeRegisteredTest extends WriteBeforeRegisteredTest { + + @Override + protected List> newFactories() { + return EpollSocketTestPermutation.INSTANCE.clientSocket(); + } +} diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslClientRenegotiateTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslClientRenegotiateTest.java new file mode 100644 index 000000000000..47431fa8a0b1 --- /dev/null +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslClientRenegotiateTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 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: + * + * http://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.channel.kqueue; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.handler.ssl.SslContext; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.SocketSslClientRenegotiateTest; + +import java.net.SocketAddress; +import java.util.List; + +public class KQueueDomainSocketSslClientRenegotiateTest extends SocketSslClientRenegotiateTest { + + public KQueueDomainSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx) { + super(serverCtx, clientCtx); + } + + @Override + protected List> newFactories() { + return KQueueSocketTestPermutation.INSTANCE.domainSocket(); + } + + @Override + protected SocketAddress newSocketAddress() { + return KQueueSocketTestPermutation.newSocketAddress(); + } +} diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslClientRenegotiateTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslClientRenegotiateTest.java new file mode 100644 index 000000000000..a804ca98eeb0 --- /dev/null +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslClientRenegotiateTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019 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: + * + * http://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.channel.kqueue; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.handler.ssl.SslContext; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.SocketSslClientRenegotiateTest; + +import java.util.List; + +public class KQueueSocketSslClientRenegotiateTest extends SocketSslClientRenegotiateTest { + + public KQueueSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx) { + super(serverCtx, clientCtx); + } + + @Override + protected List> newFactories() { + return KQueueSocketTestPermutation.INSTANCE.socket(); + } +} diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslSessionReuseTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslSessionReuseTest.java new file mode 100644 index 000000000000..5508dcb0a29a --- /dev/null +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslSessionReuseTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019 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: + * + * http://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.channel.kqueue; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.handler.ssl.SslContext; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.SocketSslSessionReuseTest; + +import java.util.List; + +public class KQueueSocketSslSessionReuseTest extends SocketSslSessionReuseTest { + + public KQueueSocketSslSessionReuseTest(SslContext serverCtx, SslContext clientCtx) { + super(serverCtx, clientCtx); + } + + @Override + protected List> newFactories() { + return KQueueSocketTestPermutation.INSTANCE.socket(); + } +} diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KqueueWriteBeforeRegisteredTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KqueueWriteBeforeRegisteredTest.java new file mode 100644 index 000000000000..b8d447565ce4 --- /dev/null +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KqueueWriteBeforeRegisteredTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019 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: + * + * http://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.channel.kqueue; + +import io.netty.bootstrap.Bootstrap; +import io.netty.testsuite.transport.TestsuitePermutation; +import io.netty.testsuite.transport.socket.WriteBeforeRegisteredTest; + +import java.util.List; + +public class KqueueWriteBeforeRegisteredTest extends WriteBeforeRegisteredTest { + + @Override + protected List> newFactories() { + return KQueueSocketTestPermutation.INSTANCE.clientSocket(); + } +} From fa6a8cb09c9679468a6c2d912ddfbbe885ee0c08 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 11 Feb 2019 09:47:44 +0100 Subject: [PATCH 376/417] =?UTF-8?q?Support=20using=20an=20Executor=20to=20?= =?UTF-8?q?offload=20blocking=20/=20long-running=20tasks=20wh=E2=80=A6=20(?= =?UTF-8?q?#8847)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: The SSLEngine does provide a way to signal to the caller that it may need to execute a blocking / long-running task which then can be offloaded to an Executor to ensure the I/O thread is not blocked. Currently how we handle this in SslHandler is not really optimal as while we offload to the Executor we still block the I/O Thread. Modifications: - Correctly support offloading the task to the Executor while suspending processing of SSL in the I/O Thread - Add new methods to SslContext to specify the Executor when creating a SslHandler - Remove @deprecated annotations from SslHandler constructor that takes an Executor - Adjust tests to also run with the Executor to ensure all works as expected. Result: Be able to offload long running tasks to an Executor when using SslHandler. Partly fixes https://github.com/netty/netty/issues/7862 and https://github.com/netty/netty/issues/7020. --- .../handler/ssl/DelegatingSslContext.java | 16 + .../ssl/ReferenceCountedOpenSslContext.java | 12 + .../java/io/netty/handler/ssl/SniHandler.java | 11 +- .../java/io/netty/handler/ssl/SslContext.java | 62 +++- .../java/io/netty/handler/ssl/SslHandler.java | 309 ++++++++++++++---- .../handler/ssl/CipherSuiteCanaryTest.java | 34 +- .../ssl/ConscryptJdkSslEngineInteropTest.java | 9 +- .../handler/ssl/ConscryptSslEngineTest.java | 9 +- .../ssl/JdkConscryptSslEngineInteropTest.java | 9 +- .../ssl/JdkOpenSslEngineInteroptTest.java | 12 +- .../netty/handler/ssl/JdkSslEngineTest.java | 14 +- .../netty/handler/ssl/OpenSslEngineTest.java | 12 +- .../ssl/OpenSslJdkSslEngineInteroptTest.java | 12 +- .../ReferenceCountedOpenSslEngineTest.java | 4 +- .../io/netty/handler/ssl/SSLEngineTest.java | 83 ++++- .../io/netty/handler/ssl/SslHandlerTest.java | 169 ++++++++++ .../SocketSslClientRenegotiateTest.java | 121 ++++--- .../socket/SocketSslGreetingTest.java | 100 +++--- ...lDomainSocketSslClientRenegotiateTest.java | 4 +- .../EpollDomainSocketSslGreetingTest.java | 4 +- .../EpollSocketSslClientRenegotiateTest.java | 4 +- .../epoll/EpollSocketSslGreetingTest.java | 4 +- ...eDomainSocketSslClientRenegotiateTest.java | 4 +- .../KQueueDomainSocketSslGreetingTest.java | 4 +- .../KQueueSocketSslClientRenegotiateTest.java | 4 +- .../kqueue/KQueueSocketSslGreetingTest.java | 4 +- 26 files changed, 800 insertions(+), 230 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/DelegatingSslContext.java b/handler/src/main/java/io/netty/handler/ssl/DelegatingSslContext.java index af027982f230..c79da85458ee 100644 --- a/handler/src/main/java/io/netty/handler/ssl/DelegatingSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/DelegatingSslContext.java @@ -21,6 +21,7 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSessionContext; import java.util.List; +import java.util.concurrent.Executor; /** * Adapter class which allows to wrap another {@link SslContext} and init {@link SSLEngine} instances. @@ -86,6 +87,21 @@ protected final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, i return handler; } + @Override + protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls, Executor executor) { + SslHandler handler = ctx.newHandler(alloc, startTls, executor); + initHandler(handler); + return handler; + } + + @Override + protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, + boolean startTls, Executor executor) { + SslHandler handler = ctx.newHandler(alloc, peerHost, peerPort, startTls, executor); + initHandler(handler); + return handler; + } + @Override public final SSLSessionContext sessionContext() { return ctx.sessionContext(); diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index da1fdb139ba2..f93544f3865a 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -44,6 +44,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -400,6 +401,17 @@ protected final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, i return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), startTls); } + @Override + protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls, Executor executor) { + return new SslHandler(newEngine0(alloc, null, -1, false), startTls, executor); + } + + @Override + protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, + boolean startTls, Executor executor) { + return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), executor); + } + SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort, boolean jdkCompatibilityMode) { return new ReferenceCountedOpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode, true); } diff --git a/handler/src/main/java/io/netty/handler/ssl/SniHandler.java b/handler/src/main/java/io/netty/handler/ssl/SniHandler.java index cda2bbdce7ae..c6a82278d823 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SniHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SniHandler.java @@ -15,6 +15,7 @@ */ package io.netty.handler.ssl; +import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.DecoderException; import io.netty.util.AsyncMapping; @@ -129,7 +130,7 @@ protected final void onLookupComplete(ChannelHandlerContext ctx, protected void replaceHandler(ChannelHandlerContext ctx, String hostname, SslContext sslContext) throws Exception { SslHandler sslHandler = null; try { - sslHandler = sslContext.newHandler(ctx.alloc()); + sslHandler = newSslHandler(sslContext, ctx.alloc()); ctx.pipeline().replace(this, SslHandler.class.getName(), sslHandler); sslHandler = null; } finally { @@ -142,6 +143,14 @@ protected void replaceHandler(ChannelHandlerContext ctx, String hostname, SslCon } } + /** + * Returns a new {@link SslHandler} using the given {@link SslContext} and {@link ByteBufAllocator}. + * Users may override this method to implement custom behavior. + */ + protected SslHandler newSslHandler(SslContext context, ByteBufAllocator allocator) { + return context.newHandler(allocator); + } + private static final class AsyncMappingAdapter implements AsyncMapping { private final Mapping mapping; diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContext.java b/handler/src/main/java/io/netty/handler/ssl/SslContext.java index 6c5a6c61096b..dab0ce745d28 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java @@ -60,6 +60,7 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.List; +import java.util.concurrent.Executor; /** * A secure socket protocol implementation which acts as a factory for {@link SSLEngine} and {@link SslHandler}. @@ -879,6 +880,22 @@ public final List nextProtocols() { */ public abstract SSLSessionContext sessionContext(); + /** + * Create a new SslHandler. + * @see #newHandler(ByteBufAllocator, Executor) + */ + public final SslHandler newHandler(ByteBufAllocator alloc) { + return newHandler(alloc, startTls); + } + + /** + * Create a new SslHandler. + * @see #newHandler(ByteBufAllocator) + */ + protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) { + return new SslHandler(newEngine(alloc), startTls); + } + /** * Creates a new {@link SslHandler}. *

    If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine @@ -900,18 +917,37 @@ public final List nextProtocols() { * SSLEngine javadocs which * limits wrap/unwrap to operate on a single SSL/TLS packet. * @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate ByteBuf objects. + * @param delegatedTaskExecutor the {@link Executor} that will be used to execute tasks that are returned by + * {@link SSLEngine#getDelegatedTask()}. * @return a new {@link SslHandler} */ - public final SslHandler newHandler(ByteBufAllocator alloc) { - return newHandler(alloc, startTls); + public SslHandler newHandler(ByteBufAllocator alloc, Executor delegatedTaskExecutor) { + return newHandler(alloc, startTls, delegatedTaskExecutor); } /** * Create a new SslHandler. - * @see #newHandler(ByteBufAllocator) + * @see #newHandler(ByteBufAllocator, String, int, boolean, Executor) */ - protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) { - return new SslHandler(newEngine(alloc), startTls); + protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls, Executor executor) { + return new SslHandler(newEngine(alloc), startTls, executor); + } + + /** + * Creates a new {@link SslHandler} + * + * @see #newHandler(ByteBufAllocator, String, int, Executor) + */ + public final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort) { + return newHandler(alloc, peerHost, peerPort, startTls); + } + + /** + * Create a new SslHandler. + * @see #newHandler(ByteBufAllocator, String, int, boolean, Executor) + */ + protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) { + return new SslHandler(newEngine(alloc, peerHost, peerPort), startTls); } /** @@ -937,19 +973,19 @@ protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) { * @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate ByteBuf objects. * @param peerHost the non-authoritative name of the host * @param peerPort the non-authoritative port + * @param delegatedTaskExecutor the {@link Executor} that will be used to execute tasks that are returned by + * {@link SSLEngine#getDelegatedTask()}. * * @return a new {@link SslHandler} */ - public final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort) { - return newHandler(alloc, peerHost, peerPort, startTls); + public SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, + Executor delegatedTaskExecutor) { + return newHandler(alloc, peerHost, peerPort, startTls, delegatedTaskExecutor); } - /** - * Create a new SslHandler. - * @see #newHandler(ByteBufAllocator, String, int, boolean) - */ - protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) { - return new SslHandler(newEngine(alloc, peerHost, peerPort), startTls); + protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls, + Executor delegatedTaskExecutor) { + return new SslHandler(newEngine(alloc, peerHost, peerPort), startTls, delegatedTaskExecutor); } /** diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index fe533735e614..367c891cf04a 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -33,6 +33,7 @@ import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromiseNotifier; import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.UnsupportedMessageTypeException; import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCounted; @@ -55,10 +56,9 @@ import java.nio.channels.ClosedChannelException; import java.nio.channels.DatagramChannel; import java.nio.channels.SocketChannel; -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; @@ -390,6 +390,7 @@ abstract SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, private boolean flushedBeforeHandshake; private boolean readDuringHandshake; private boolean handshakeStarted; + private SslHandlerCoalescingBufferQueue pendingUnencryptedWrites; private Promise handshakePromise = new LazyChannelPromise(); private final LazyChannelPromise sslClosePromise = new LazyChannelPromise(); @@ -402,6 +403,7 @@ abstract SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, private boolean outboundClosed; private boolean closeNotify; + private boolean processTask; private int packetLength; @@ -417,7 +419,7 @@ abstract SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, volatile int wrapDataSize = MAX_PLAINTEXT_LENGTH; /** - * Creates a new instance. + * Creates a new instance which runs all delegated tasks directly on the {@link EventExecutor}. * * @param engine the {@link SSLEngine} this handler will use */ @@ -426,29 +428,36 @@ public SslHandler(SSLEngine engine) { } /** - * Creates a new instance. + * Creates a new instance which runs all delegated tasks directly on the {@link EventExecutor}. * * @param engine the {@link SSLEngine} this handler will use * @param startTls {@code true} if the first write request shouldn't be * encrypted by the {@link SSLEngine} */ - @SuppressWarnings("deprecation") public SslHandler(SSLEngine engine, boolean startTls) { this(engine, startTls, ImmediateExecutor.INSTANCE); } /** - * @deprecated Use {@link #SslHandler(SSLEngine)} instead. + * Creates a new instance. + * + * @param engine the {@link SSLEngine} this handler will use + * @param delegatedTaskExecutor the {@link Executor} that will be used to execute tasks that are returned by + * {@link SSLEngine#getDelegatedTask()}. */ - @Deprecated public SslHandler(SSLEngine engine, Executor delegatedTaskExecutor) { this(engine, false, delegatedTaskExecutor); } /** - * @deprecated Use {@link #SslHandler(SSLEngine, boolean)} instead. + * Creates a new instance. + * + * @param engine the {@link SSLEngine} this handler will use + * @param startTls {@code true} if the first write request shouldn't be + * encrypted by the {@link SSLEngine} + * @param delegatedTaskExecutor the {@link Executor} that will be used to execute tasks that are returned by + * {@link SSLEngine#getDelegatedTask()}. */ - @Deprecated public SslHandler(SSLEngine engine, boolean startTls, Executor delegatedTaskExecutor) { if (engine == null) { throw new NullPointerException("engine"); @@ -774,6 +783,10 @@ public void flush(ChannelHandlerContext ctx) throws Exception { return; } + if (processTask) { + return; + } + try { wrapAndFlush(ctx); } catch (Throwable cause) { @@ -813,7 +826,7 @@ private void wrap(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLExcepti final int wrapDataSize = this.wrapDataSize; // Only continue to loop if the handler was not removed in the meantime. // See https://github.com/netty/netty/issues/5860 - while (!ctx.isRemoved()) { + outer: while (!ctx.isRemoved()) { promise = ctx.newPromise(); buf = wrapDataSize > 0 ? pendingUnencryptedWrites.remove(alloc, wrapDataSize, promise) : @@ -850,7 +863,11 @@ private void wrap(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLExcepti switch (result.getHandshakeStatus()) { case NEED_TASK: - runDelegatedTasks(); + if (!runDelegatedTasks(inUnwrap)) { + // We scheduled a task on the delegatingTaskExecutor, so stop processing as we will + // resume once the task completes. + break outer; + } break; case FINISHED: setHandshakeSuccess(); @@ -919,7 +936,7 @@ private boolean wrapNonAppData(ChannelHandlerContext ctx, boolean inUnwrap) thro try { // Only continue to loop if the handler was not removed in the meantime. // See https://github.com/netty/netty/issues/5860 - while (!ctx.isRemoved()) { + outer: while (!ctx.isRemoved()) { if (out == null) { // As this is called for the handshake we have no real idea how big the buffer needs to be. // That said 2048 should give us enough room to include everything like ALPN / NPN data. @@ -941,7 +958,11 @@ private boolean wrapNonAppData(ChannelHandlerContext ctx, boolean inUnwrap) thro setHandshakeSuccess(); return false; case NEED_TASK: - runDelegatedTasks(); + if (!runDelegatedTasks(inUnwrap)) { + // We scheduled a task on the delegatingTaskExecutor, so stop processing as we will + // resume once the task completes. + break outer; + } break; case NEED_UNWRAP: if (inUnwrap) { @@ -1243,6 +1264,9 @@ private void handleUnwrapThrowable(ChannelHandlerContext ctx, Throwable cause) { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws SSLException { + if (processTask) { + return; + } if (jdkCompatibilityMode) { decodeJdkCompatible(ctx, in); } else { @@ -1252,6 +1276,10 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) t @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + channelReadComplete0(ctx); + } + + private void channelReadComplete0(ChannelHandlerContext ctx) { // Discard bytes of the cumulation buffer if needed. discardSomeReadBytes(); @@ -1366,7 +1394,16 @@ private int unwrap( } break; case NEED_TASK: - runDelegatedTasks(); + if (!runDelegatedTasks(true)) { + // We scheduled a task on the delegatingTaskExecutor, so stop processing as we will + // resume once the task completes. + // + // We break out of the loop only and do NOT return here as we still may need to notify + // about the closure of the SSLEngine. + // + wrapLater = false; + break unwrapLoop; + } break; case FINISHED: setHandshakeSuccess(); @@ -1447,65 +1484,223 @@ private static ByteBuffer toByteBuffer(ByteBuf out, int index, int len) { out.nioBuffer(index, len); } + private static boolean inEventLoop(Executor executor) { + return executor instanceof EventExecutor && ((EventExecutor) executor).inEventLoop(); + } + + private static void runAllDelegatedTasks(SSLEngine engine) { + for (;;) { + Runnable task = engine.getDelegatedTask(); + if (task == null) { + return; + } + task.run(); + } + } + /** - * Fetches all delegated tasks from the {@link SSLEngine} and runs them via the {@link #delegatedTaskExecutor}. - * If the {@link #delegatedTaskExecutor} is {@link ImmediateExecutor}, just call {@link Runnable#run()} directly - * instead of using {@link Executor#execute(Runnable)}. Otherwise, run the tasks via - * the {@link #delegatedTaskExecutor} and wait until the tasks are finished. + * Will either run the delegated task directly calling {@link Runnable#run()} and return {@code true} or will + * offload the delegated task using {@link Executor#execute(Runnable)} and return {@code false}. + * + * If the task is offloaded it will take care to resume its work on the {@link EventExecutor} once there are no + * more tasks to process. */ - private void runDelegatedTasks() { - if (delegatedTaskExecutor == ImmediateExecutor.INSTANCE) { + private boolean runDelegatedTasks(boolean inUnwrap) { + if (delegatedTaskExecutor == ImmediateExecutor.INSTANCE || inEventLoop(delegatedTaskExecutor)) { + // We should run the task directly in the EventExecutor thread and not offload at all. for (;;) { - Runnable task = engine.getDelegatedTask(); - if (task == null) { - break; - } - - task.run(); + runAllDelegatedTasks(engine); + return true; } } else { - final List tasks = new ArrayList(2); - for (;;) { - final Runnable task = engine.getDelegatedTask(); - if (task == null) { - break; + executeDelegatedTasks(inUnwrap); + return false; + } + } + + private void executeDelegatedTasks(boolean inUnwrap) { + processTask = true; + try { + delegatedTaskExecutor.execute(new SslTasksRunner(inUnwrap)); + } catch (RejectedExecutionException e) { + processTask = false; + throw e; + } + } + + /** + * {@link Runnable} that will be scheduled on the {@code delegatedTaskExecutor} and will take care + * of resume work on the {@link EventExecutor} once the task was executed. + */ + private final class SslTasksRunner implements Runnable { + private final boolean inUnwrap; + + SslTasksRunner(boolean inUnwrap) { + this.inUnwrap = inUnwrap; + } + + // Handle errors which happened during task processing. + private void taskError(Throwable e) { + if (inUnwrap) { + // As the error happened while the task was scheduled as part of unwrap(...) we also need to ensure + // we fire it through the pipeline as inbound error to be consistent with what we do in decode(...). + // + // This will also ensure we fail the handshake future and flush all produced data. + try { + handleUnwrapThrowable(ctx, e); + } catch (Throwable cause) { + safeExceptionCaught(cause); } + } else { + setHandshakeFailure(ctx, e); + forceFlush(ctx); + } + } - tasks.add(task); + // Try to call exceptionCaught(...) + private void safeExceptionCaught(Throwable cause) { + try { + exceptionCaught(ctx, wrapIfNeeded(cause)); + } catch (Throwable error) { + ctx.fireExceptionCaught(error); } + } - if (tasks.isEmpty()) { - return; + private Throwable wrapIfNeeded(Throwable cause) { + if (!inUnwrap) { + // If we are not in unwrap(...) we can just rethrow without wrapping at all. + return cause; + } + // As the exception would have been triggered by an inbound operation we will need to wrap it in a + // DecoderException to mimic what a decoder would do when decode(...) throws. + return cause instanceof DecoderException ? cause : new DecoderException(cause); + } + + private void tryDecodeAgain() { + try { + channelRead(ctx, Unpooled.EMPTY_BUFFER); + } catch (Throwable cause) { + safeExceptionCaught(cause); + } finally { + // As we called channelRead(...) we also need to call channelReadComplete(...) which + // will ensure we either call ctx.fireChannelReadComplete() or will trigger a ctx.read() if + // more data is needed. + channelReadComplete0(ctx); } + } - final CountDownLatch latch = new CountDownLatch(1); - delegatedTaskExecutor.execute(new Runnable() { - @Override - public void run() { - try { - for (Runnable task: tasks) { - task.run(); + /** + * Executed after the wrapped {@code task} was executed via {@code delegatedTaskExecutor} to resume work + * on the {@link EventExecutor}. + */ + private void resumeOnEventExecutor() { + assert ctx.executor().inEventLoop(); + + processTask = false; + + try { + HandshakeStatus status = engine.getHandshakeStatus(); + switch (status) { + // There is another task that needs to be executed and offloaded to the delegatingTaskExecutor. + case NEED_TASK: + executeDelegatedTasks(inUnwrap); + + break; + + // The handshake finished, lets notify about the completion of it and resume processing. + case FINISHED: + setHandshakeSuccess(); + + // deliberate fall-through + + // Not handshaking anymore, lets notify about the completion if not done yet and resume processing. + case NOT_HANDSHAKING: + setHandshakeSuccessIfStillHandshaking(); + try { + // Lets call wrap to ensure we produce the alert if there is any pending and also to + // ensure we flush any queued data.. + wrap(ctx, inUnwrap); + } catch (Throwable e) { + taskError(e); + return; } - } catch (Exception e) { - ctx.fireExceptionCaught(e); - } finally { - latch.countDown(); - } - } - }); - boolean interrupted = false; - while (latch.getCount() != 0) { - try { - latch.await(); - } catch (InterruptedException e) { - // Interrupt later. - interrupted = true; + // Flush now as we may have written some data as part of the wrap call. + forceFlush(ctx); + + tryDecodeAgain(); + break; + + // We need more data so lets try to unwrap first and then call decode again which will feed us + // with buffered data (if there is any). + case NEED_UNWRAP: + unwrapNonAppData(ctx); + tryDecodeAgain(); + break; + + // To make progress we need to call SSLEngine.wrap(...) which may produce more output data + // that will be written to the Channel. + case NEED_WRAP: + try { + wrapNonAppData(ctx, inUnwrap); + } catch (Throwable e) { + taskError(e); + return; + } + // Flush now as we may have written some data as part of the wrap call. + forceFlush(ctx); + + // Now try to feed in more data that we have buffered. + tryDecodeAgain(); + break; + default: + // Should never reach here as we handle all cases. + throw new AssertionError(); } + } catch (Throwable cause) { + safeExceptionCaught(cause); + } + } + + @Override + public void run() { + try { + runAllDelegatedTasks(engine); + + // All tasks were processed. + assert engine.getHandshakeStatus() != HandshakeStatus.NEED_TASK; + + // Jump back on the EventExecutor. + ctx.executor().execute(new Runnable() { + @Override + public void run() { + resumeOnEventExecutor(); + } + }); + } catch (final Throwable cause) { + handleException(cause); } + } - if (interrupted) { - Thread.currentThread().interrupt(); + private void handleException(final Throwable cause) { + if (ctx.executor().inEventLoop()) { + processTask = false; + safeExceptionCaught(cause); + } else { + try { + ctx.executor().execute(new Runnable() { + @Override + public void run() { + processTask = false; + safeExceptionCaught(cause); + } + }); + } catch (RejectedExecutionException ignore) { + processTask = false; + // the context itself will handle the rejected exception when try to schedule the operation so + // ignore the RejectedExecutionException + ctx.fireExceptionCaught(cause); + } } } } diff --git a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java index 9c394ccf6371..855cb848b798 100644 --- a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java @@ -18,6 +18,7 @@ import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; @@ -41,6 +42,9 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.junit.AfterClass; @@ -66,7 +70,7 @@ public class CipherSuiteCanaryTest { private static SelfSignedCertificate CERT; - @Parameters(name = "{index}: serverSslProvider = {0}, clientSslProvider = {1}, rfcCipherName = {2}") + @Parameters(name = "{index}: serverSslProvider = {0}, clientSslProvider = {1}, rfcCipherName = {2}, delegate = {3}") public static Collection parameters() { List dst = new ArrayList(); dst.addAll(expand("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")); // DHE-RSA-AES128-GCM-SHA256 @@ -80,7 +84,7 @@ public static void init() throws Exception { } @AfterClass - public static void destory() { + public static void destroy() { GROUP.shutdownGracefully(); CERT.delete(); } @@ -90,11 +94,14 @@ public static void destory() { private final SslProvider clientSslProvider; private final String rfcCipherName; + private final boolean delegate; - public CipherSuiteCanaryTest(SslProvider serverSslProvider, SslProvider clientSslProvider, String rfcCipherName) { + public CipherSuiteCanaryTest(SslProvider serverSslProvider, SslProvider clientSslProvider, + String rfcCipherName, boolean delegate) { this.serverSslProvider = serverSslProvider; this.clientSslProvider = clientSslProvider; this.rfcCipherName = rfcCipherName; + this.delegate = delegate; } private static void assumeCipherAvailable(SslProvider provider, String cipher) throws NoSuchAlgorithmException { @@ -113,6 +120,14 @@ private static void assumeCipherAvailable(SslProvider provider, String cipher) t Assume.assumeTrue("Unsupported cipher: " + cipher, cipherSupported); } + private static SslHandler newSslHandler(SslContext sslCtx, ByteBufAllocator allocator, Executor executor) { + if (executor == null) { + return sslCtx.newHandler(allocator); + } else { + return sslCtx.newHandler(allocator, executor); + } + } + @Test public void testHandshake() throws Exception { // Check if the cipher is supported at all which may not be the case for various JDK versions and OpenSSL API @@ -129,6 +144,8 @@ public void testHandshake() throws Exception { .protocols(SslUtils.PROTOCOL_TLS_V1_2) .build(); + final ExecutorService executorService = delegate ? Executors.newCachedThreadPool() : null; + try { final SslContext sslClientContext = SslContextBuilder.forClient() .sslProvider(clientSslProvider) @@ -146,7 +163,7 @@ public void testHandshake() throws Exception { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast(sslServerContext.newHandler(ch.alloc())); + pipeline.addLast(newSslHandler(sslServerContext, ch.alloc(), executorService)); pipeline.addLast(new SimpleChannelInboundHandler() { @Override @@ -182,7 +199,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast(sslClientContext.newHandler(ch.alloc())); + pipeline.addLast(newSslHandler(sslClientContext, ch.alloc(), executorService)); pipeline.addLast(new SimpleChannelInboundHandler() { @Override @@ -229,6 +246,10 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) } } finally { ReferenceCountUtil.release(sslServerContext); + + if (executorService != null) { + executorService.shutdown(); + } } } @@ -267,7 +288,8 @@ private static List expand(String rfcCipherName) { continue; } - dst.add(new Object[]{serverSslProvider, clientSslProvider, rfcCipherName}); + dst.add(new Object[]{serverSslProvider, clientSslProvider, rfcCipherName, true}); + dst.add(new Object[]{serverSslProvider, clientSslProvider, rfcCipherName, false}); } } diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java index 0976264d7a12..6a31dac906e7 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java @@ -31,17 +31,18 @@ @RunWith(Parameterized.class) public class ConscryptJdkSslEngineInteropTest extends SSLEngineTest { - @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}, delegate = {2}") public static Collection data() { List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), false }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), true }); } return params; } - public ConscryptJdkSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo) { - super(type, combo); + public ConscryptJdkSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo, boolean delegate) { + super(type, combo, delegate); } @BeforeClass diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java index 7d068409905b..15a68bdf61ef 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java @@ -30,17 +30,18 @@ @RunWith(Parameterized.class) public class ConscryptSslEngineTest extends SSLEngineTest { - @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}, delegate = {2}") public static Collection data() { List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), false }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), true }); } return params; } - public ConscryptSslEngineTest(BufferType type, ProtocolCipherCombo combo) { - super(type, combo); + public ConscryptSslEngineTest(BufferType type, ProtocolCipherCombo combo, boolean delegate) { + super(type, combo, delegate); } @BeforeClass diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java index 309490af592a..771713a95593 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java @@ -32,17 +32,18 @@ @RunWith(Parameterized.class) public class JdkConscryptSslEngineInteropTest extends SSLEngineTest { - @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}, delegate = {2}") public static Collection data() { List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), false }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), true }); } return params; } - public JdkConscryptSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo) { - super(type, combo); + public JdkConscryptSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo, boolean delegate) { + super(type, combo, delegate); } @BeforeClass diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java index 45b3c7e5e088..677730c47579 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java @@ -33,21 +33,23 @@ @RunWith(Parameterized.class) public class JdkOpenSslEngineInteroptTest extends SSLEngineTest { - @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}, delegate = {2}") public static Collection data() { List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), false }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), true }); if (PlatformDependent.javaVersion() >= 11 && OpenSsl.isTlsv13Supported()) { - params.add(new Object[] { type, ProtocolCipherCombo.tlsv13() }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv13(), false }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv13(), true }); } } return params; } - public JdkOpenSslEngineInteroptTest(BufferType type, ProtocolCipherCombo protocolCipherCombo) { - super(type, protocolCipherCombo); + public JdkOpenSslEngineInteroptTest(BufferType type, ProtocolCipherCombo protocolCipherCombo, boolean delegate) { + super(type, protocolCipherCombo, delegate); } @BeforeClass diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java index 74f000fd01e0..db86298932bf 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java @@ -142,14 +142,17 @@ final void activate(JdkSslEngineTest instance) { private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1"; private static final String APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE = "my-protocol-FOO"; - @Parameterized.Parameters(name = "{index}: providerType = {0}, bufferType = {1}, combo = {2}") + @Parameterized.Parameters(name = "{index}: providerType = {0}, bufferType = {1}, combo = {2}, delegate = {3}") public static Collection data() { List params = new ArrayList(); for (ProviderType providerType : ProviderType.values()) { for (BufferType bufferType : BufferType.values()) { - params.add(new Object[]{ providerType, bufferType, ProtocolCipherCombo.tlsv12()}); + params.add(new Object[]{ providerType, bufferType, ProtocolCipherCombo.tlsv12(), true }); + params.add(new Object[]{ providerType, bufferType, ProtocolCipherCombo.tlsv12(), false }); + if (PlatformDependent.javaVersion() >= 11) { - params.add(new Object[] { providerType, bufferType, ProtocolCipherCombo.tlsv13() }); + params.add(new Object[] { providerType, bufferType, ProtocolCipherCombo.tlsv13(), true }); + params.add(new Object[] { providerType, bufferType, ProtocolCipherCombo.tlsv13(), false }); } } } @@ -160,8 +163,9 @@ public static Collection data() { private Provider provider; - public JdkSslEngineTest(ProviderType providerType, BufferType bufferType, ProtocolCipherCombo protocolCipherCombo) { - super(bufferType, protocolCipherCombo); + public JdkSslEngineTest(ProviderType providerType, BufferType bufferType, + ProtocolCipherCombo protocolCipherCombo, boolean delegate) { + super(bufferType, protocolCipherCombo, delegate); this.providerType = providerType; } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index 32be76781ad3..3ee9f4bfe495 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -67,21 +67,23 @@ public class OpenSslEngineTest extends SSLEngineTest { private static final String PREFERRED_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http2"; private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1"; - @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}, delegate = {2}") public static Collection data() { List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), false }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), true }); if (OpenSsl.isTlsv13Supported()) { - params.add(new Object[] { type, ProtocolCipherCombo.tlsv13() }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv13(), false }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv13(), true }); } } return params; } - public OpenSslEngineTest(BufferType type, ProtocolCipherCombo cipherCombo) { - super(type, cipherCombo); + public OpenSslEngineTest(BufferType type, ProtocolCipherCombo cipherCombo, boolean delegate) { + super(type, cipherCombo, delegate); } @BeforeClass diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java index df4e757e505b..0bd9af843fb6 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java @@ -35,21 +35,23 @@ @RunWith(Parameterized.class) public class OpenSslJdkSslEngineInteroptTest extends SSLEngineTest { - @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}") + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}, delegate = {2}") public static Collection data() { List params = new ArrayList(); for (BufferType type: BufferType.values()) { - params.add(new Object[] { type, ProtocolCipherCombo.tlsv12()}); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), false }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), true }); if (PlatformDependent.javaVersion() >= 11 && OpenSsl.isTlsv13Supported()) { - params.add(new Object[] { type, ProtocolCipherCombo.tlsv13() }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv13(), false }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv13(), true }); } } return params; } - public OpenSslJdkSslEngineInteroptTest(BufferType type, ProtocolCipherCombo combo) { - super(type, combo); + public OpenSslJdkSslEngineInteroptTest(BufferType type, ProtocolCipherCombo combo, boolean delegate) { + super(type, combo, delegate); } @BeforeClass diff --git a/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java index 588619d3a7cb..1728a53844a2 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java @@ -23,8 +23,8 @@ public class ReferenceCountedOpenSslEngineTest extends OpenSslEngineTest { - public ReferenceCountedOpenSslEngineTest(BufferType type, ProtocolCipherCombo combo) { - super(type, combo); + public ReferenceCountedOpenSslEngineTest(BufferType type, ProtocolCipherCombo combo, boolean delegate) { + super(type, combo, delegate); } @Override diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index c1a4e12056b9..1148a0a06fe0 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -73,6 +73,8 @@ import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import javax.net.ssl.KeyManagerFactory; @@ -252,10 +254,13 @@ public String toString() { private final BufferType type; private final ProtocolCipherCombo protocolCipherCombo; + private final boolean delegate; + private ExecutorService delegatingExecutor; - protected SSLEngineTest(BufferType type, ProtocolCipherCombo protocolCipherCombo) { + protected SSLEngineTest(BufferType type, ProtocolCipherCombo protocolCipherCombo, boolean delegate) { this.type = type; this.protocolCipherCombo = protocolCipherCombo; + this.delegate = delegate; } protected ByteBuffer allocateBuffer(int len) { @@ -441,6 +446,9 @@ public void setup() { MockitoAnnotations.initMocks(this); serverLatch = new CountDownLatch(1); clientLatch = new CountDownLatch(1); + if (delegate) { + delegatingExecutor = Executors.newCachedThreadPool(); + } } @After @@ -500,6 +508,10 @@ public void tearDown() throws InterruptedException { clientGroupShutdownFuture.sync(); } serverException = null; + + if (delegatingExecutor != null) { + delegatingExecutor.shutdown(); + } } @Test @@ -703,7 +715,8 @@ protected void initChannel(Channel ch) throws Exception { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ChannelPipeline p = ch.pipeline(); - SslHandler handler = serverSslCtx.newHandler(ch.alloc()); + SslHandler handler = delegatingExecutor == null ? serverSslCtx.newHandler(ch.alloc()) : + serverSslCtx.newHandler(ch.alloc(), delegatingExecutor); if (serverInitEngine) { mySetupMutualAuthServerInitSslHandler(handler); } @@ -746,7 +759,10 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E protected void initChannel(Channel ch) throws Exception { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ChannelPipeline p = ch.pipeline(); - p.addLast(clientSslCtx.newHandler(ch.alloc())); + + SslHandler handler = delegatingExecutor == null ? clientSslCtx.newHandler(ch.alloc()) : + clientSslCtx.newHandler(ch.alloc(), delegatingExecutor); + p.addLast(handler); p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch)); p.addLast(new ChannelInboundHandlerAdapter() { @Override @@ -849,7 +865,10 @@ private void mySetupClientHostnameValidation(File serverCrtFile, File serverKeyF protected void initChannel(Channel ch) throws Exception { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ChannelPipeline p = ch.pipeline(); - p.addLast(serverSslCtx.newHandler(ch.alloc())); + + SslHandler handler = delegatingExecutor == null ? serverSslCtx.newHandler(ch.alloc()) : + serverSslCtx.newHandler(ch.alloc(), delegatingExecutor); + p.addLast(handler); p.addLast(new MessageDelegatorChannelHandler(serverReceiver, serverLatch)); p.addLast(new ChannelInboundHandlerAdapter() { @Override @@ -889,7 +908,11 @@ protected void initChannel(Channel ch) throws Exception { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ChannelPipeline p = ch.pipeline(); InetSocketAddress remoteAddress = (InetSocketAddress) serverChannel.localAddress(); - SslHandler sslHandler = clientSslCtx.newHandler(ch.alloc(), expectedHost, 0); + + SslHandler sslHandler = delegatingExecutor == null ? + clientSslCtx.newHandler(ch.alloc(), expectedHost, 0) : + clientSslCtx.newHandler(ch.alloc(), expectedHost, 0, delegatingExecutor); + SSLParameters parameters = sslHandler.engine().getSSLParameters(); if (SslUtils.isValidHostNameForSNI(expectedHost)) { assertEquals(1, parameters.getServerNames().size()); @@ -1053,7 +1076,10 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc protected void initChannel(Channel ch) throws Exception { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); - final SslHandler handler = clientSslCtx.newHandler(ch.alloc()); + final SslHandler handler = delegatingExecutor == null ? + clientSslCtx.newHandler(ch.alloc()) : + clientSslCtx.newHandler(ch.alloc(), delegatingExecutor); + handler.engine().setNeedClientAuth(true); ChannelPipeline p = ch.pipeline(); p.addLast(handler); @@ -1125,7 +1151,7 @@ private static void writeAndVerifyReceived(ByteBuf message, Channel sendChannel, MessageReceiver receiver) throws Exception { List dataCapture = null; try { - assertTrue(sendChannel.writeAndFlush(message).await(5, TimeUnit.SECONDS)); + assertTrue(sendChannel.writeAndFlush(message).await(50, TimeUnit.SECONDS)); receiverLatch.await(5, TimeUnit.SECONDS); message.resetReaderIndex(); ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); @@ -1248,7 +1274,12 @@ public void initChannel(SocketChannel ch) { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ChannelPipeline p = ch.pipeline(); - p.addLast(serverSslCtx.newHandler(ch.alloc())); + + SslHandler handler = delegatingExecutor == null ? + serverSslCtx.newHandler(ch.alloc()) : + serverSslCtx.newHandler(ch.alloc(), delegatingExecutor); + + p.addLast(handler); p.addLast(new ChannelInboundHandlerAdapter() { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { @@ -1304,7 +1335,11 @@ public void initChannel(SocketChannel ch) { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ChannelPipeline p = ch.pipeline(); - SslHandler sslHandler = clientSslCtx.newHandler(ch.alloc()); + + SslHandler sslHandler = delegatingExecutor == null ? + clientSslCtx.newHandler(ch.alloc()) : + clientSslCtx.newHandler(ch.alloc(), delegatingExecutor); + // The renegotiate is not expected to succeed, so we should stop trying in a timely manner so // the unit test can terminate relativley quicly. sslHandler.setHandshakeTimeout(1, TimeUnit.SECONDS); @@ -1384,7 +1419,7 @@ protected void testEnablingAnAlreadyDisabledSslProtocol(String[] protocols1, Str } } - protected void handshake(SSLEngine clientEngine, SSLEngine serverEngine) throws SSLException { + protected void handshake(SSLEngine clientEngine, SSLEngine serverEngine) throws Exception { ByteBuffer cTOs = allocateBuffer(clientEngine.getSession().getPacketBufferSize()); ByteBuffer sTOc = allocateBuffer(serverEngine.getSession().getPacketBufferSize()); @@ -1477,14 +1512,18 @@ private static boolean isHandshakeFinished(SSLEngineResult result) { return result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED; } - private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) { + private void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) throws Exception { if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) { for (;;) { Runnable task = engine.getDelegatedTask(); if (task == null) { break; } - task.run(); + if (delegatingExecutor == null) { + task.run(); + } else { + delegatingExecutor.submit(task).get(); + } } } } @@ -1586,7 +1625,12 @@ protected void initChannel(Channel ch) throws Exception { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ChannelPipeline p = ch.pipeline(); - p.addLast(serverSslCtx.newHandler(ch.alloc())); + + SslHandler sslHandler = delegatingExecutor == null ? + serverSslCtx.newHandler(ch.alloc()) : + serverSslCtx.newHandler(ch.alloc(), delegatingExecutor); + + p.addLast(sslHandler); p.addLast(new MessageDelegatorChannelHandler(serverReceiver, serverLatch)); p.addLast(new ChannelInboundHandlerAdapter() { @Override @@ -1611,7 +1655,12 @@ protected void initChannel(Channel ch) throws Exception { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); ChannelPipeline p = ch.pipeline(); - p.addLast(clientSslCtx.newHandler(ch.alloc())); + + SslHandler sslHandler = delegatingExecutor == null ? + clientSslCtx.newHandler(ch.alloc()) : + clientSslCtx.newHandler(ch.alloc(), delegatingExecutor); + + p.addLast(sslHandler); p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch)); p.addLast(new ChannelInboundHandlerAdapter() { @Override @@ -1661,7 +1710,11 @@ public void testMutualAuthSameCertChain() throws Exception { protected void initChannel(Channel ch) throws Exception { ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); - ch.pipeline().addFirst(serverSslCtx.newHandler(ch.alloc())); + SslHandler sslHandler = delegatingExecutor == null ? + serverSslCtx.newHandler(ch.alloc()) : + serverSslCtx.newHandler(ch.alloc(), delegatingExecutor); + + ch.pipeline().addFirst(sslHandler); ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { diff --git a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java index 772a15f9ebcf..6749fe4ebc61 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java @@ -53,6 +53,8 @@ import io.netty.util.ReferenceCounted; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.ImmediateEventExecutor; +import io.netty.util.concurrent.ImmediateExecutor; import io.netty.util.concurrent.Promise; import org.hamcrest.CoreMatchers; import org.junit.Test; @@ -63,6 +65,9 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -824,4 +829,168 @@ public void operationComplete(ChannelFuture future) throws Exception { ReferenceCountUtil.release(sslClientCtx); } } + + @Test + public void testHandshakeWithExecutorThatExecuteDirecty() throws Exception { + testHandshakeWithExecutor(new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }); + } + + @Test + public void testHandshakeWithImmediateExecutor() throws Exception { + testHandshakeWithExecutor(ImmediateExecutor.INSTANCE); + } + + @Test + public void testHandshakeWithImmediateEventExecutor() throws Exception { + testHandshakeWithExecutor(ImmediateEventExecutor.INSTANCE); + } + + @Test + public void testHandshakeWithExecutor() throws Exception { + ExecutorService executorService = Executors.newCachedThreadPool(); + try { + testHandshakeWithExecutor(executorService); + } finally { + executorService.shutdown(); + } + } + + private void testHandshakeWithExecutor(Executor executor) throws Exception { + final SslContext sslClientCtx = SslContextBuilder.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(SslProvider.JDK).build(); + + final SelfSignedCertificate cert = new SelfSignedCertificate(); + final SslContext sslServerCtx = SslContextBuilder.forServer(cert.key(), cert.cert()) + .sslProvider(SslProvider.JDK).build(); + + EventLoopGroup group = new NioEventLoopGroup(); + Channel sc = null; + Channel cc = null; + final SslHandler clientSslHandler = sslClientCtx.newHandler(UnpooledByteBufAllocator.DEFAULT, executor); + final SslHandler serverSslHandler = sslServerCtx.newHandler(UnpooledByteBufAllocator.DEFAULT, executor); + + try { + sc = new ServerBootstrap() + .group(group) + .channel(NioServerSocketChannel.class) + .childHandler(serverSslHandler) + .bind(new InetSocketAddress(0)).syncUninterruptibly().channel(); + + ChannelFuture future = new Bootstrap() + .group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addLast(clientSslHandler); + } + }).connect(sc.localAddress()); + cc = future.syncUninterruptibly().channel(); + + assertTrue(clientSslHandler.handshakeFuture().await().isSuccess()); + assertTrue(serverSslHandler.handshakeFuture().await().isSuccess()); + } finally { + if (cc != null) { + cc.close().syncUninterruptibly(); + } + if (sc != null) { + sc.close().syncUninterruptibly(); + } + group.shutdownGracefully(); + ReferenceCountUtil.release(sslClientCtx); + } + } + + @Test + public void testClientHandshakeTimeoutBecauseExecutorNotExecute() throws Exception { + testHandshakeTimeoutBecauseExecutorNotExecute(true); + } + + @Test + public void testServerHandshakeTimeoutBecauseExecutorNotExecute() throws Exception { + testHandshakeTimeoutBecauseExecutorNotExecute(false); + } + + private void testHandshakeTimeoutBecauseExecutorNotExecute(final boolean client) throws Exception { + final SslContext sslClientCtx = SslContextBuilder.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .sslProvider(SslProvider.JDK).build(); + + final SelfSignedCertificate cert = new SelfSignedCertificate(); + final SslContext sslServerCtx = SslContextBuilder.forServer(cert.key(), cert.cert()) + .sslProvider(SslProvider.JDK).build(); + + EventLoopGroup group = new NioEventLoopGroup(); + Channel sc = null; + Channel cc = null; + final SslHandler clientSslHandler = sslClientCtx.newHandler(UnpooledByteBufAllocator.DEFAULT, new Executor() { + @Override + public void execute(Runnable command) { + if (!client) { + command.run(); + } + // Do nothing to simulate slow execution. + } + }); + if (client) { + clientSslHandler.setHandshakeTimeout(100, TimeUnit.MILLISECONDS); + } + final SslHandler serverSslHandler = sslServerCtx.newHandler(UnpooledByteBufAllocator.DEFAULT, new Executor() { + @Override + public void execute(Runnable command) { + if (client) { + command.run(); + } + // Do nothing to simulate slow execution. + } + }); + if (!client) { + serverSslHandler.setHandshakeTimeout(100, TimeUnit.MILLISECONDS); + } + try { + sc = new ServerBootstrap() + .group(group) + .channel(NioServerSocketChannel.class) + .childHandler(serverSslHandler) + .bind(new InetSocketAddress(0)).syncUninterruptibly().channel(); + + ChannelFuture future = new Bootstrap() + .group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addLast(clientSslHandler); + } + }).connect(sc.localAddress()); + cc = future.syncUninterruptibly().channel(); + + if (client) { + Throwable cause = clientSslHandler.handshakeFuture().await().cause(); + assertThat(cause, CoreMatchers.instanceOf(SSLException.class)); + assertThat(cause.getMessage(), containsString("timed out")); + assertFalse(serverSslHandler.handshakeFuture().await().isSuccess()); + } else { + Throwable cause = serverSslHandler.handshakeFuture().await().cause(); + assertThat(cause, CoreMatchers.instanceOf(SSLException.class)); + assertThat(cause.getMessage(), containsString("timed out")); + assertFalse(clientSslHandler.handshakeFuture().await().isSuccess()); + } + } finally { + if (cc != null) { + cc.close().syncUninterruptibly(); + } + if (sc != null) { + sc.close().syncUninterruptibly(); + } + group.shutdownGracefully(); + ReferenceCountUtil.release(sslClientCtx); + } + } } diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java index 8036f081f4a7..4574ca70951e 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslClientRenegotiateTest.java @@ -18,6 +18,7 @@ import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; @@ -46,6 +47,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLHandshakeException; @@ -73,7 +77,7 @@ public class SocketSslClientRenegotiateTest extends AbstractSocketTest { KEY_FILE = ssc.privateKey(); } - @Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}") + @Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}, delegate = {2}") public static Collection data() throws Exception { List serverContexts = new ArrayList(); List clientContexts = new ArrayList(); @@ -91,7 +95,8 @@ public static Collection data() throws Exception { for (SslContext sc: serverContexts) { for (SslContext cc: clientContexts) { for (int i = 0; i < 32; i++) { - params.add(new Object[] { sc, cc}); + params.add(new Object[] { sc, cc, true}); + params.add(new Object[] { sc, cc, false}); } } } @@ -101,6 +106,7 @@ public static Collection data() throws Exception { private final SslContext serverCtx; private final SslContext clientCtx; + private final boolean delegate; private final AtomicReference clientException = new AtomicReference(); private final AtomicReference serverException = new AtomicReference(); @@ -116,9 +122,10 @@ public static Collection data() throws Exception { private final TestHandler serverHandler = new TestHandler(serverException); public SocketSslClientRenegotiateTest( - SslContext serverCtx, SslContext clientCtx) { + SslContext serverCtx, SslContext clientCtx, boolean delegate) { this.serverCtx = serverCtx; this.clientCtx = clientCtx; + this.delegate = delegate; } @Test(timeout = 30000) @@ -129,58 +136,74 @@ public void testSslRenegotiationRejected() throws Throwable { run(); } + private static SslHandler newSslHandler(SslContext sslCtx, ByteBufAllocator allocator, Executor executor) { + if (executor == null) { + return sslCtx.newHandler(allocator); + } else { + return sslCtx.newHandler(allocator, executor); + } + } + public void testSslRenegotiationRejected(ServerBootstrap sb, Bootstrap cb) throws Throwable { reset(); - sb.childHandler(new ChannelInitializer() { - @Override - @SuppressWarnings("deprecation") - public void initChannel(Channel sch) throws Exception { - serverChannel = sch; - serverSslHandler = serverCtx.newHandler(sch.alloc()); - // As we test renegotiation we should use a protocol that support it. - serverSslHandler.engine().setEnabledProtocols(new String[] { "TLSv1.2" }); - sch.pipeline().addLast("ssl", serverSslHandler); - sch.pipeline().addLast("handler", serverHandler); - } - }); - - cb.handler(new ChannelInitializer() { - @Override - @SuppressWarnings("deprecation") - public void initChannel(Channel sch) throws Exception { - clientChannel = sch; - clientSslHandler = clientCtx.newHandler(sch.alloc()); - // As we test renegotiation we should use a protocol that support it. - clientSslHandler.engine().setEnabledProtocols(new String[] { "TLSv1.2" }); - sch.pipeline().addLast("ssl", clientSslHandler); - sch.pipeline().addLast("handler", clientHandler); - } - }); - - Channel sc = sb.bind().sync().channel(); - cb.connect(sc.localAddress()).sync(); + final ExecutorService executorService = delegate ? Executors.newCachedThreadPool() : null; - Future clientHandshakeFuture = clientSslHandler.handshakeFuture(); - clientHandshakeFuture.sync(); - - String renegotiation = clientSslHandler.engine().getEnabledCipherSuites()[0]; - // Use the first previous enabled ciphersuite and try to renegotiate. - clientSslHandler.engine().setEnabledCipherSuites(new String[] { renegotiation }); - clientSslHandler.renegotiate().await(); - serverChannel.close().awaitUninterruptibly(); - clientChannel.close().awaitUninterruptibly(); - sc.close().awaitUninterruptibly(); try { - if (serverException.get() != null) { - throw serverException.get(); + sb.childHandler(new ChannelInitializer() { + @Override + @SuppressWarnings("deprecation") + public void initChannel(Channel sch) throws Exception { + serverChannel = sch; + serverSslHandler = newSslHandler(serverCtx, sch.alloc(), executorService); + // As we test renegotiation we should use a protocol that support it. + serverSslHandler.engine().setEnabledProtocols(new String[]{"TLSv1.2"}); + sch.pipeline().addLast("ssl", serverSslHandler); + sch.pipeline().addLast("handler", serverHandler); + } + }); + + cb.handler(new ChannelInitializer() { + @Override + @SuppressWarnings("deprecation") + public void initChannel(Channel sch) throws Exception { + clientChannel = sch; + clientSslHandler = newSslHandler(clientCtx, sch.alloc(), executorService); + // As we test renegotiation we should use a protocol that support it. + clientSslHandler.engine().setEnabledProtocols(new String[]{"TLSv1.2"}); + sch.pipeline().addLast("ssl", clientSslHandler); + sch.pipeline().addLast("handler", clientHandler); + } + }); + + Channel sc = sb.bind().sync().channel(); + cb.connect(sc.localAddress()).sync(); + + Future clientHandshakeFuture = clientSslHandler.handshakeFuture(); + clientHandshakeFuture.sync(); + + String renegotiation = clientSslHandler.engine().getEnabledCipherSuites()[0]; + // Use the first previous enabled ciphersuite and try to renegotiate. + clientSslHandler.engine().setEnabledCipherSuites(new String[]{renegotiation}); + clientSslHandler.renegotiate().await(); + serverChannel.close().awaitUninterruptibly(); + clientChannel.close().awaitUninterruptibly(); + sc.close().awaitUninterruptibly(); + try { + if (serverException.get() != null) { + throw serverException.get(); + } + fail(); + } catch (DecoderException e) { + assertTrue(e.getCause() instanceof SSLHandshakeException); + } + if (clientException.get() != null) { + throw clientException.get(); + } + } finally { + if (executorService != null) { + executorService.shutdown(); } - fail(); - } catch (DecoderException e) { - assertTrue(e.getCause() instanceof SSLHandshakeException); - } - if (clientException.get() != null) { - throw clientException.get(); } } diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslGreetingTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslGreetingTest.java index d7db90be8b8a..b3f0e46f45d5 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslGreetingTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslGreetingTest.java @@ -18,6 +18,7 @@ import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; @@ -48,6 +49,9 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.assertEquals; @@ -74,7 +78,7 @@ public class SocketSslGreetingTest extends AbstractSocketTest { KEY_FILE = ssc.privateKey(); } - @Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}") + @Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}, delegate = {2}") public static Collection data() throws Exception { List serverContexts = new ArrayList(); serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE).sslProvider(SslProvider.JDK).build()); @@ -95,7 +99,8 @@ public static Collection data() throws Exception { List params = new ArrayList(); for (SslContext sc: serverContexts) { for (SslContext cc: clientContexts) { - params.add(new Object[] { sc, cc }); + params.add(new Object[] { sc, cc, true }); + params.add(new Object[] { sc, cc, false }); } } return params; @@ -103,10 +108,20 @@ public static Collection data() throws Exception { private final SslContext serverCtx; private final SslContext clientCtx; + private final boolean delegate; - public SocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx) { + public SocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx, boolean delegate) { this.serverCtx = serverCtx; this.clientCtx = clientCtx; + this.delegate = delegate; + } + + private static SslHandler newSslHandler(SslContext sslCtx, ByteBufAllocator allocator, Executor executor) { + if (executor == null) { + return sslCtx.newHandler(allocator); + } else { + return sslCtx.newHandler(allocator, executor); + } } // Test for https://github.com/netty/netty/pull/2437 @@ -119,46 +134,53 @@ public void testSslGreeting(ServerBootstrap sb, Bootstrap cb) throws Throwable { final ServerHandler sh = new ServerHandler(); final ClientHandler ch = new ClientHandler(); - sb.childHandler(new ChannelInitializer() { - @Override - public void initChannel(Channel sch) throws Exception { - ChannelPipeline p = sch.pipeline(); - p.addLast(serverCtx.newHandler(sch.alloc())); - p.addLast(new LoggingHandler(LOG_LEVEL)); - p.addLast(sh); - } - }); - - cb.handler(new ChannelInitializer() { - @Override - public void initChannel(Channel sch) throws Exception { - ChannelPipeline p = sch.pipeline(); - p.addLast(clientCtx.newHandler(sch.alloc())); - p.addLast(new LoggingHandler(LOG_LEVEL)); - p.addLast(ch); - } - }); + final ExecutorService executorService = delegate ? Executors.newCachedThreadPool() : null; + try { + sb.childHandler(new ChannelInitializer() { + @Override + public void initChannel(Channel sch) throws Exception { + ChannelPipeline p = sch.pipeline(); + p.addLast(newSslHandler(serverCtx, sch.alloc(), executorService)); + p.addLast(new LoggingHandler(LOG_LEVEL)); + p.addLast(sh); + } + }); + + cb.handler(new ChannelInitializer() { + @Override + public void initChannel(Channel sch) throws Exception { + ChannelPipeline p = sch.pipeline(); + p.addLast(newSslHandler(clientCtx, sch.alloc(), executorService)); + p.addLast(new LoggingHandler(LOG_LEVEL)); + p.addLast(ch); + } + }); - Channel sc = sb.bind().sync().channel(); - Channel cc = cb.connect(sc.localAddress()).sync().channel(); + Channel sc = sb.bind().sync().channel(); + Channel cc = cb.connect(sc.localAddress()).sync().channel(); - ch.latch.await(); + ch.latch.await(); - sh.channel.close().awaitUninterruptibly(); - cc.close().awaitUninterruptibly(); - sc.close().awaitUninterruptibly(); + sh.channel.close().awaitUninterruptibly(); + cc.close().awaitUninterruptibly(); + sc.close().awaitUninterruptibly(); - if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) { - throw sh.exception.get(); - } - if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) { - throw ch.exception.get(); - } - if (sh.exception.get() != null) { - throw sh.exception.get(); - } - if (ch.exception.get() != null) { - throw ch.exception.get(); + if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) { + throw sh.exception.get(); + } + if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) { + throw ch.exception.get(); + } + if (sh.exception.get() != null) { + throw sh.exception.get(); + } + if (ch.exception.get() != null) { + throw ch.exception.get(); + } + } finally { + if (executorService != null) { + executorService.shutdown(); + } } } diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslClientRenegotiateTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslClientRenegotiateTest.java index 9037c18adf6f..3f493d08355b 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslClientRenegotiateTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslClientRenegotiateTest.java @@ -26,8 +26,8 @@ public class EpollDomainSocketSslClientRenegotiateTest extends SocketSslClientRenegotiateTest { - public EpollDomainSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx) { - super(serverCtx, clientCtx); + public EpollDomainSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx, boolean delegate) { + super(serverCtx, clientCtx, delegate); } @Override diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslGreetingTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslGreetingTest.java index a1ed0c5ac2ea..4683a701eaae 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslGreetingTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketSslGreetingTest.java @@ -26,8 +26,8 @@ public class EpollDomainSocketSslGreetingTest extends SocketSslGreetingTest { - public EpollDomainSocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx) { - super(serverCtx, clientCtx); + public EpollDomainSocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx, boolean delegate) { + super(serverCtx, clientCtx, delegate); } @Override diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslClientRenegotiateTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslClientRenegotiateTest.java index abe74d2ff5a4..3f69196f12fe 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslClientRenegotiateTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslClientRenegotiateTest.java @@ -25,8 +25,8 @@ public class EpollSocketSslClientRenegotiateTest extends SocketSslClientRenegotiateTest { - public EpollSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx) { - super(serverCtx, clientCtx); + public EpollSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx, boolean delegate) { + super(serverCtx, clientCtx, delegate); } @Override diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslGreetingTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslGreetingTest.java index 21d86b4fc7fb..34bf98a150f6 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslGreetingTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketSslGreetingTest.java @@ -25,8 +25,8 @@ public class EpollSocketSslGreetingTest extends SocketSslGreetingTest { - public EpollSocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx) { - super(serverCtx, clientCtx); + public EpollSocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx, boolean delegate) { + super(serverCtx, clientCtx, delegate); } @Override diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslClientRenegotiateTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslClientRenegotiateTest.java index 47431fa8a0b1..a719b7c73ef7 100644 --- a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslClientRenegotiateTest.java +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslClientRenegotiateTest.java @@ -26,8 +26,8 @@ public class KQueueDomainSocketSslClientRenegotiateTest extends SocketSslClientRenegotiateTest { - public KQueueDomainSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx) { - super(serverCtx, clientCtx); + public KQueueDomainSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx, boolean delegate) { + super(serverCtx, clientCtx, delegate); } @Override diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslGreetingTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslGreetingTest.java index 492021a52ba1..8cbaa00efa29 100644 --- a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslGreetingTest.java +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueDomainSocketSslGreetingTest.java @@ -26,8 +26,8 @@ public class KQueueDomainSocketSslGreetingTest extends SocketSslGreetingTest { - public KQueueDomainSocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx) { - super(serverCtx, clientCtx); + public KQueueDomainSocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx, boolean delegate) { + super(serverCtx, clientCtx, delegate); } @Override diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslClientRenegotiateTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslClientRenegotiateTest.java index a804ca98eeb0..a3ba23818198 100644 --- a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslClientRenegotiateTest.java +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslClientRenegotiateTest.java @@ -25,8 +25,8 @@ public class KQueueSocketSslClientRenegotiateTest extends SocketSslClientRenegotiateTest { - public KQueueSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx) { - super(serverCtx, clientCtx); + public KQueueSocketSslClientRenegotiateTest(SslContext serverCtx, SslContext clientCtx, boolean delegate) { + super(serverCtx, clientCtx, delegate); } @Override diff --git a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslGreetingTest.java b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslGreetingTest.java index 9242fc388f78..6eecc35a5196 100644 --- a/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslGreetingTest.java +++ b/transport-native-kqueue/src/test/java/io/netty/channel/kqueue/KQueueSocketSslGreetingTest.java @@ -25,8 +25,8 @@ public class KQueueSocketSslGreetingTest extends SocketSslGreetingTest { - public KQueueSocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx) { - super(serverCtx, clientCtx); + public KQueueSocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx, boolean delegate) { + super(serverCtx, clientCtx, delegate); } @Override From c68e85b749b5433634dab59823a9748cea72fdf5 Mon Sep 17 00:00:00 2001 From: Rukshani Athapathu Date: Tue, 12 Feb 2019 21:35:30 +0530 Subject: [PATCH 377/417] Fix h2c upgrade failure when multiple connection headers are present in upgrade request (#8848) Motivation: When more than one connection header is present in h2c upgrade request, upgrade fails. This is to fix that. Modification: In HttpServerUpgradeHandler's upgrade() method, check whether any of the connection header value is upgrade, not just the first header value which might return a different value other than upgrade. Result: Fixes #8846. With this PR, now when multiple connection headers are sent with the upgrade request, upgrade will not fail. --- .../codec/http/HttpServerUpgradeHandler.java | 21 ++-- ...leartextHttp2ServerUpgradeHandlerTest.java | 114 ++++++++++++------ 2 files changed, 88 insertions(+), 47 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java index f1f3efcb1de7..2b54b0e4b211 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java @@ -14,9 +14,6 @@ */ package io.netty.handler.codec.http; -import static io.netty.util.AsciiString.containsContentEqualsIgnoreCase; -import static io.netty.util.AsciiString.containsAllContentEqualsIgnoreCase; - import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -30,7 +27,10 @@ import static io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; +import static io.netty.util.AsciiString.containsAllContentEqualsIgnoreCase; +import static io.netty.util.AsciiString.containsContentEqualsIgnoreCase; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.StringUtil.COMMA; /** * A server-side handler that receives HTTP requests and optionally performs a protocol switch if @@ -284,16 +284,23 @@ private boolean upgrade(final ChannelHandlerContext ctx, final FullHttpRequest r } // Make sure the CONNECTION header is present. - CharSequence connectionHeader = request.headers().get(HttpHeaderNames.CONNECTION); - if (connectionHeader == null) { + List connectionHeaderValues = request.headers().getAll(HttpHeaderNames.CONNECTION); + + if (connectionHeaderValues == null) { return false; } + final StringBuilder concatenatedConnectionValue = new StringBuilder(connectionHeaderValues.size() * 10); + for (CharSequence connectionHeaderValue : connectionHeaderValues) { + concatenatedConnectionValue.append(connectionHeaderValue).append(COMMA); + } + concatenatedConnectionValue.setLength(concatenatedConnectionValue.length() - 1); + // Make sure the CONNECTION header contains UPGRADE as well as all protocol-specific headers. Collection requiredHeaders = upgradeCodec.requiredUpgradeHeaders(); - List values = splitHeader(connectionHeader); + List values = splitHeader(concatenatedConnectionValue); if (!containsContentEqualsIgnoreCase(values, HttpHeaderNames.UPGRADE) || - !containsAllContentEqualsIgnoreCase(values, requiredHeaders)) { + !containsAllContentEqualsIgnoreCase(values, requiredHeaders)) { return false; } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/CleartextHttp2ServerUpgradeHandlerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/CleartextHttp2ServerUpgradeHandlerTest.java index a544054b6685..129f62d7574d 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/CleartextHttp2ServerUpgradeHandlerTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/CleartextHttp2ServerUpgradeHandlerTest.java @@ -42,8 +42,15 @@ import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * Tests for {@link CleartextHttp2ServerUpgradeHandler} @@ -112,47 +119,35 @@ public void priorKnowledge() throws Exception { @Test public void upgrade() throws Exception { - setUpServerChannel(); - String upgradeString = "GET / HTTP/1.1\r\n" + - "Host: example.com\r\n" + - "Connection: Upgrade, HTTP2-Settings\r\n" + - "Upgrade: h2c\r\n" + - "HTTP2-Settings: AAMAAABkAAQAAP__\r\n\r\n"; - ByteBuf upgrade = Unpooled.copiedBuffer(upgradeString, CharsetUtil.US_ASCII); - - assertFalse(channel.writeInbound(upgrade)); - - assertEquals(1, userEvents.size()); - - Object userEvent = userEvents.get(0); - assertTrue(userEvent instanceof UpgradeEvent); - assertEquals("h2c", ((UpgradeEvent) userEvent).protocol()); - ReferenceCountUtil.release(userEvent); - - assertEquals(100, http2ConnectionHandler.connection().local().maxActiveStreams()); - assertEquals(65535, http2ConnectionHandler.connection().local().flowController().initialWindowSize()); - - assertEquals(1, http2ConnectionHandler.connection().numActiveStreams()); - assertNotNull(http2ConnectionHandler.connection().stream(1)); - - Http2Stream stream = http2ConnectionHandler.connection().stream(1); - assertEquals(State.HALF_CLOSED_REMOTE, stream.state()); - assertFalse(stream.isHeadersSent()); - - String expectedHttpResponse = "HTTP/1.1 101 Switching Protocols\r\n" + - "connection: upgrade\r\n" + - "upgrade: h2c\r\n\r\n"; - ByteBuf responseBuffer = channel.readOutbound(); - assertEquals(expectedHttpResponse, responseBuffer.toString(CharsetUtil.UTF_8)); - responseBuffer.release(); + "Host: example.com\r\n" + + "Connection: Upgrade, HTTP2-Settings\r\n" + + "Upgrade: h2c\r\n" + + "HTTP2-Settings: AAMAAABkAAQAAP__\r\n\r\n"; + validateClearTextUpgrade(upgradeString); + } - // Check that the preface was send (a.k.a the settings frame) - ByteBuf settingsBuffer = channel.readOutbound(); - assertNotNull(settingsBuffer); - settingsBuffer.release(); + @Test + public void upgradeWithMultipleConnectionHeaders() { + String upgradeString = "GET / HTTP/1.1\r\n" + + "Host: example.com\r\n" + + "Connection: keep-alive\r\n" + + "Connection: Upgrade, HTTP2-Settings\r\n" + + "Upgrade: h2c\r\n" + + "HTTP2-Settings: AAMAAABkAAQAAP__\r\n\r\n"; + validateClearTextUpgrade(upgradeString); + } - assertNull(channel.readOutbound()); + @Test + public void requiredHeadersInSeparateConnectionHeaders() { + String upgradeString = "GET / HTTP/1.1\r\n" + + "Host: example.com\r\n" + + "Connection: keep-alive\r\n" + + "Connection: HTTP2-Settings\r\n" + + "Connection: Upgrade\r\n" + + "Upgrade: h2c\r\n" + + "HTTP2-Settings: AAMAAABkAAQAAP__\r\n\r\n"; + validateClearTextUpgrade(upgradeString); } @Test @@ -254,4 +249,43 @@ private static ByteBuf settingsFrameBuf() { private static Http2Settings expectedSettings() { return new Http2Settings().maxConcurrentStreams(100).initialWindowSize(65535); } + + private void validateClearTextUpgrade(String upgradeString) { + setUpServerChannel(); + + ByteBuf upgrade = Unpooled.copiedBuffer(upgradeString, CharsetUtil.US_ASCII); + + assertFalse(channel.writeInbound(upgrade)); + + assertEquals(1, userEvents.size()); + + Object userEvent = userEvents.get(0); + assertTrue(userEvent instanceof UpgradeEvent); + assertEquals("h2c", ((UpgradeEvent) userEvent).protocol()); + ReferenceCountUtil.release(userEvent); + + assertEquals(100, http2ConnectionHandler.connection().local().maxActiveStreams()); + assertEquals(65535, http2ConnectionHandler.connection().local().flowController().initialWindowSize()); + + assertEquals(1, http2ConnectionHandler.connection().numActiveStreams()); + assertNotNull(http2ConnectionHandler.connection().stream(1)); + + Http2Stream stream = http2ConnectionHandler.connection().stream(1); + assertEquals(State.HALF_CLOSED_REMOTE, stream.state()); + assertFalse(stream.isHeadersSent()); + + String expectedHttpResponse = "HTTP/1.1 101 Switching Protocols\r\n" + + "connection: upgrade\r\n" + + "upgrade: h2c\r\n\r\n"; + ByteBuf responseBuffer = channel.readOutbound(); + assertEquals(expectedHttpResponse, responseBuffer.toString(CharsetUtil.UTF_8)); + responseBuffer.release(); + + // Check that the preface was send (a.k.a the settings frame) + ByteBuf settingsBuffer = channel.readOutbound(); + assertNotNull(settingsBuffer); + settingsBuffer.release(); + + assertNull(channel.readOutbound()); + } } From 8fecbab2c56d3f49d0353d58ee1681f3e6d3feca Mon Sep 17 00:00:00 2001 From: Artem Morozov Date: Thu, 14 Feb 2019 04:14:58 +0300 Subject: [PATCH 378/417] Handle null "origin" header in "Old Hixie 75 handshake" as proper bad request. (#8864) Motivation: Gracefully respond on bad client request. We have a set of errors produced by Android 7.1.1/7.1.2 clients where both headers `HttpHeaderNames.SEC_WEBSOCKET_VERSION` and `HttpHeaderNames.ORIGIN` are not present. Absence of the first headers leads to WebSocketServerHandshaker00 be applied as a handshaker. However, null 2nd header causes ``` java.lang.NullPointerException: value io.netty.util.internal.ObjectUtil.checkNotNull(ObjectUtil.java:33) io.netty.handler.codec.DefaultHeaders.addObject(DefaultHeaders.java:327) io.netty.handler.codec.http.DefaultHttpHeaders.add(DefaultHttpHeaders.java:123) io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker00.newHandshakeResponse(WebSocketServerHandshaker00.java:162) ``` Which causes connection close with unclear reason. Modification: Added null-check, and in case of null an appropriate WebSocketHandshakeException is thrown. Result: In case of null `HttpHeaderNames.ORIGIN` header a WebSocketHandshakeException is caught by WebSocketServerProtocolHandler which sends a graceful `BAD_REQUEST`. --- .../WebSocketServerHandshaker00.java | 6 +++- .../WebSocketServerHandshaker00Test.java | 32 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java index ff1797db6426..1d06d9cc06a1 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java @@ -159,7 +159,11 @@ protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders res.content().writeBytes(WebSocketUtil.md5(input.array())); } else { // Old Hixie 75 handshake getMethod with no challenge: - res.headers().add(HttpHeaderNames.WEBSOCKET_ORIGIN, req.headers().get(HttpHeaderNames.ORIGIN)); + String origin = req.headers().get(HttpHeaderNames.ORIGIN); + if (origin == null) { + throw new WebSocketHandshakeException("Missing origin header, got only " + req.headers().names()); + } + res.headers().add(HttpHeaderNames.WEBSOCKET_ORIGIN, origin); res.headers().add(HttpHeaderNames.WEBSOCKET_LOCATION, uri()); String protocol = req.headers().get(HttpHeaderNames.WEBSOCKET_PROTOCOL); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java index 76826aba8b06..8783e0b5bbc5 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java @@ -32,7 +32,9 @@ import org.junit.Assert; import org.junit.Test; -import static io.netty.handler.codec.http.HttpVersion.*; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; public class WebSocketServerHandshaker00Test { @@ -46,6 +48,34 @@ public void testPerformOpeningHandshakeSubProtocolNotSupported() { testPerformOpeningHandshake0(false); } + @Test + public void testPerformHandshakeWithoutOriginHeader() { + EmbeddedChannel ch = new EmbeddedChannel( + new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); + + FullHttpRequest req = new DefaultFullHttpRequest( + HTTP_1_1, HttpMethod.GET, "/chat", Unpooled.copiedBuffer("^n:ds[4U", CharsetUtil.US_ASCII)); + + req.headers().set(HttpHeaderNames.HOST, "server.example.com"); + req.headers().set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET); + req.headers().set(HttpHeaderNames.CONNECTION, "Upgrade"); + req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_KEY1, "4 @1 46546xW%0l 1 5"); + req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, "chat, superchat"); + + WebSocketServerHandshaker00 handshaker00 = new WebSocketServerHandshaker00( + "ws://example.com/chat", "chat", Integer.MAX_VALUE); + try { + handshaker00.handshake(ch, req); + fail("Expecting WebSocketHandshakeException"); + } catch (WebSocketHandshakeException e) { + assertEquals("Missing origin header, got only " + + "[host, upgrade, connection, sec-websocket-key1, sec-websocket-protocol]", + e.getMessage()); + } finally { + req.release(); + } + } + private static void testPerformOpeningHandshake0(boolean subProtocol) { EmbeddedChannel ch = new EmbeddedChannel( new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); From 098705040d53179524e21164ac1ed758542ed292 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Thu, 14 Feb 2019 15:18:37 -0800 Subject: [PATCH 379/417] Log the shaded form of native workdir system property (#8867) Motivation: When users' /tmp is noexec, NativeLibraryLoader logs a message informing them how to fix the problem by setting a system property. However, if Netty has been shaded that message will tell them to set the un-shaded system property name, which won't work. Modifications: Change the code to let shading tools rename the native.workdir property name reference within user-visible log messages. Notably, debug logs were _not_ changed, as there's many debug statements including a variety of property names. Fixing them would be a much more invasive change and have limited benefit. Result: The users will see the correctly-named system property to set if they are using a noexec /tmp. --- .../java/io/netty/util/internal/NativeLibraryLoader.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java b/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java index 31b4a465fa5d..e74ca391619b 100644 --- a/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java +++ b/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java @@ -217,10 +217,13 @@ public static void load(String originalName, ClassLoader loader) { try { if (tmpFile != null && tmpFile.isFile() && tmpFile.canRead() && !NoexecVolumeDetector.canExecuteExecutable(tmpFile)) { + // Pass "io.netty.native.workdir" as an argument to allow shading tools to see + // the string. Since this is printed out to users to tell them what to do next, + // we want the value to be correct even when shading. logger.info("{} exists but cannot be executed even when execute permissions set; " + - "check volume for \"noexec\" flag; use -Dio.netty.native.workdir=[path] " + + "check volume for \"noexec\" flag; use -D{}=[path] " + "to set native working directory separately.", - tmpFile.getPath()); + tmpFile.getPath(), "io.netty.native.workdir"); } } catch (Throwable t) { suppressed.add(t); From 1c6191c16636c162b21b38106e9a6289abbbcc7c Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 15 Feb 2019 09:38:36 -0800 Subject: [PATCH 380/417] Do not depend on the implementation detail of Unpooled.buffer(int) when accessing backing array. (#8865) Motivation: We should not depend on the implementation detail of Unpooled.buffer(int) to allocate the exact size of backing byte[] as depending on the implementation it may return a buffer with a bigger backing array. Modifications: Explicit allocate the byte[] and wrap it in the ByteBuf. This way we are sure that ByteBuf.array() returns an byte[] which has the exact length and content we expect. Result: More correct and safe usage of ByteBuf.array() --- .../codec/http/websocketx/WebSocketServerHandshaker00.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java index 1d06d9cc06a1..994cd09103ac 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java @@ -152,7 +152,7 @@ protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) / BEGINNING_SPACE.matcher(key2).replaceAll("").length()); long c = req.content().readLong(); - ByteBuf input = Unpooled.buffer(16); + ByteBuf input = Unpooled.wrappedBuffer(new byte[16]).setIndex(0, 0); input.writeInt(a); input.writeInt(b); input.writeLong(c); From f176384a729c1d9352c9ed878b9b967ca2f31bf8 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 15 Feb 2019 13:13:17 -0800 Subject: [PATCH 381/417] Include the original Exception that caused the Channel to be closed in the ClosedChannelException (#8863) Motivation: To make it easier to understand why a Channel was closed previously and so why the operation failed with a ClosedChannelException we should include the original Exception. Modifications: - Store the original exception that lead to the closed Channel and include it in the ClosedChannelException that is used to fail the operation. - Add unit test Result: Fixes https://github.com/netty/netty/issues/8862. --- .../io/netty/channel/AbstractChannel.java | 45 ++++++++-- .../ExtendedClosedChannelException.java | 32 +++++++ .../io/netty/channel/AbstractChannelTest.java | 89 ++++++++++++++++--- 3 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 transport/src/main/java/io/netty/channel/ExtendedClosedChannelException.java diff --git a/transport/src/main/java/io/netty/channel/AbstractChannel.java b/transport/src/main/java/io/netty/channel/AbstractChannel.java index 11c14a9624be..8f8fe2f787a2 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannel.java @@ -44,14 +44,14 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractChannel.class); - private static final ClosedChannelException FLUSH0_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace( - new ClosedChannelException(), AbstractUnsafe.class, "flush0()"); private static final ClosedChannelException ENSURE_OPEN_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace( - new ClosedChannelException(), AbstractUnsafe.class, "ensureOpen(...)"); + new ExtendedClosedChannelException(null), AbstractUnsafe.class, "ensureOpen(...)"); private static final ClosedChannelException CLOSE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace( new ClosedChannelException(), AbstractUnsafe.class, "close(...)"); private static final ClosedChannelException WRITE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace( - new ClosedChannelException(), AbstractUnsafe.class, "write(...)"); + new ExtendedClosedChannelException(null), AbstractUnsafe.class, "write(...)"); + private static final ClosedChannelException FLUSH0_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace( + new ExtendedClosedChannelException(null), AbstractUnsafe.class, "flush0()"); private static final NotYetConnectedException FLUSH0_NOT_YET_CONNECTED_EXCEPTION = ThrowableUtil.unknownStackTrace( new NotYetConnectedException(), AbstractUnsafe.class, "flush0()"); @@ -67,6 +67,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha private volatile EventLoop eventLoop; private volatile boolean registered; private boolean closeInitiated; + private Throwable initialCloseCause; /** Cache for the string representation of this channel */ private boolean strValActive; @@ -870,7 +871,7 @@ public final void write(Object msg, ChannelPromise promise) { // need to fail the future right away. If it is not null the handling of the rest // will be done in flush0() // See https://github.com/netty/netty/issues/2362 - safeSetFailure(promise, WRITE_CLOSED_CHANNEL_EXCEPTION); + safeSetFailure(promise, newWriteException(initialCloseCause)); // release message now to prevent resource-leak ReferenceCountUtil.release(msg); return; @@ -926,7 +927,7 @@ protected void flush0() { outboundBuffer.failFlushed(FLUSH0_NOT_YET_CONNECTED_EXCEPTION, true); } else { // Do not trigger channelWritabilityChanged because the channel is closed already. - outboundBuffer.failFlushed(FLUSH0_CLOSED_CHANNEL_EXCEPTION, false); + outboundBuffer.failFlushed(newFlush0Exception(initialCloseCause), false); } } finally { inFlush0 = false; @@ -946,12 +947,14 @@ protected void flush0() { * This is needed as otherwise {@link #isActive()} , {@link #isOpen()} and {@link #isWritable()} * may still return {@code true} even if the channel should be closed as result of the exception. */ - close(voidPromise(), t, FLUSH0_CLOSED_CHANNEL_EXCEPTION, false); + initialCloseCause = t; + close(voidPromise(), t, newFlush0Exception(t), false); } else { try { shutdownOutput(voidPromise(), t); } catch (Throwable t2) { - close(voidPromise(), t2, FLUSH0_CLOSED_CHANNEL_EXCEPTION, false); + initialCloseCause = t; + close(voidPromise(), t2, newFlush0Exception(t), false); } } } finally { @@ -959,6 +962,30 @@ protected void flush0() { } } + private ClosedChannelException newWriteException(Throwable cause) { + if (cause == null) { + return WRITE_CLOSED_CHANNEL_EXCEPTION; + } + return ThrowableUtil.unknownStackTrace( + new ExtendedClosedChannelException(cause), AbstractUnsafe.class, "write(...)"); + } + + private ClosedChannelException newFlush0Exception(Throwable cause) { + if (cause == null) { + return FLUSH0_CLOSED_CHANNEL_EXCEPTION; + } + return ThrowableUtil.unknownStackTrace( + new ExtendedClosedChannelException(cause), AbstractUnsafe.class, "flush0()"); + } + + private ClosedChannelException newEnsureOpenException(Throwable cause) { + if (cause == null) { + return ENSURE_OPEN_CLOSED_CHANNEL_EXCEPTION; + } + return ThrowableUtil.unknownStackTrace( + new ExtendedClosedChannelException(cause), AbstractUnsafe.class, "ensureOpen(...)"); + } + @Override public final ChannelPromise voidPromise() { assertEventLoop(); @@ -971,7 +998,7 @@ protected final boolean ensureOpen(ChannelPromise promise) { return true; } - safeSetFailure(promise, ENSURE_OPEN_CLOSED_CHANNEL_EXCEPTION); + safeSetFailure(promise, newEnsureOpenException(initialCloseCause)); return false; } diff --git a/transport/src/main/java/io/netty/channel/ExtendedClosedChannelException.java b/transport/src/main/java/io/netty/channel/ExtendedClosedChannelException.java new file mode 100644 index 000000000000..3b908cd1930a --- /dev/null +++ b/transport/src/main/java/io/netty/channel/ExtendedClosedChannelException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2019 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: + * + * http://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.channel; + +import java.nio.channels.ClosedChannelException; + +final class ExtendedClosedChannelException extends ClosedChannelException { + + ExtendedClosedChannelException(Throwable cause) { + if (cause != null) { + initCause(cause); + } + } + + @Override + public Throwable fillInStackTrace() { + return this; + } +} diff --git a/transport/src/test/java/io/netty/channel/AbstractChannelTest.java b/transport/src/test/java/io/netty/channel/AbstractChannelTest.java index fc5fb9b066d9..9d5110ea8d8e 100644 --- a/transport/src/test/java/io/netty/channel/AbstractChannelTest.java +++ b/transport/src/test/java/io/netty/channel/AbstractChannelTest.java @@ -15,8 +15,12 @@ */ package io.netty.channel; +import java.io.IOException; +import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.nio.channels.ClosedChannelException; +import io.netty.util.NetUtil; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -82,6 +86,69 @@ public void ensureDefaultChannelId() { assertTrue(channelId instanceof DefaultChannelId); } + @Test + public void testClosedChannelExceptionCarryIOException() throws Exception { + final IOException ioException = new IOException(); + final Channel channel = new TestChannel() { + private boolean open = true; + private boolean active; + + @Override + protected AbstractUnsafe newUnsafe() { + return new AbstractUnsafe() { + @Override + public void connect( + SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { + active = true; + promise.setSuccess(); + } + }; + } + + @Override + protected void doClose() { + active = false; + open = false; + } + + @Override + protected void doWrite(ChannelOutboundBuffer in) throws Exception { + throw ioException; + } + + @Override + public boolean isOpen() { + return open; + } + + @Override + public boolean isActive() { + return active; + } + }; + + EventLoop loop = new DefaultEventLoop(); + try { + registerChannel(loop, channel); + channel.connect(new InetSocketAddress(NetUtil.LOCALHOST, 8888)).sync(); + assertSame(ioException, channel.writeAndFlush("").await().cause()); + + assertClosedChannelException(channel.writeAndFlush(""), ioException); + assertClosedChannelException(channel.write(""), ioException); + assertClosedChannelException(channel.bind(new InetSocketAddress(NetUtil.LOCALHOST, 8888)), ioException); + } finally { + channel.close(); + loop.shutdownGracefully(); + } + } + + private static void assertClosedChannelException(ChannelFuture future, IOException expected) + throws InterruptedException { + Throwable cause = future.await().cause(); + assertTrue(cause instanceof ClosedChannelException); + assertSame(expected, cause.getCause()); + } + private static void registerChannel(EventLoop eventLoop, Channel channel) throws Exception { DefaultChannelPromise future = new DefaultChannelPromise(channel); channel.unsafe().register(eventLoop, future); @@ -90,11 +157,8 @@ private static void registerChannel(EventLoop eventLoop, Channel channel) throws private static class TestChannel extends AbstractChannel { private static final ChannelMetadata TEST_METADATA = new ChannelMetadata(false); - private class TestUnsafe extends AbstractUnsafe { - @Override - public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { } - } + private final ChannelConfig config = new DefaultChannelConfig(this); TestChannel() { super(null); @@ -102,7 +166,7 @@ public void connect(SocketAddress remoteAddress, SocketAddress localAddress, Cha @Override public ChannelConfig config() { - return new DefaultChannelConfig(this); + return config; } @Override @@ -122,7 +186,12 @@ public ChannelMetadata metadata() { @Override protected AbstractUnsafe newUnsafe() { - return new TestUnsafe(); + return new AbstractUnsafe() { + @Override + public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { + promise.setFailure(new UnsupportedOperationException()); + } + }; } @Override @@ -141,16 +210,16 @@ protected SocketAddress remoteAddress0() { } @Override - protected void doBind(SocketAddress localAddress) throws Exception { } + protected void doBind(SocketAddress localAddress) { } @Override - protected void doDisconnect() throws Exception { } + protected void doDisconnect() { } @Override - protected void doClose() throws Exception { } + protected void doClose() { } @Override - protected void doBeginRead() throws Exception { } + protected void doBeginRead() { } @Override protected void doWrite(ChannelOutboundBuffer in) throws Exception { } From d02b51965f6b4906ca1f8f9648c2eb801df7b94a Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 25 Feb 2019 08:55:55 +0100 Subject: [PATCH 382/417] Don't deregister Channel as part of closing it when using native kqueue transport (#8881) Motivation: In https://github.com/netty/netty/pull/8665 we changed how we handle the registration of Channels to KQueue but missed to removed some code which would deregister the Channel before it actual closed the underlying socket. This could lead to have events triggered still while not have a mapping to the Channel anymore. Modifications: Remove deregister call during socket closure. Result: Fixes https://github.com/netty/netty/issues/8849. --- .../channel/kqueue/AbstractKQueueChannel.java | 30 +------------------ 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java index be07852f3783..f44a7f164f8b 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueChannel.java @@ -124,35 +124,7 @@ protected void doClose() throws Exception { // Even if we allow half closed sockets we should give up on reading. Otherwise we may allow a read attempt on a // socket which has not even been connected yet. This has been observed to block during unit tests. inputClosedSeenErrorOnRead = true; - try { - if (isRegistered()) { - // The FD will be closed, which should take care of deleting any associated events from kqueue, but - // since we rely upon jniSelfRef to be consistent we make sure that we clear this reference out for - // all events which are pending in kqueue to avoid referencing a deleted pointer at a later time. - - // Need to check if we are on the EventLoop as doClose() may be triggered by the GlobalEventExecutor - // if SO_LINGER is used. - // - // See https://github.com/netty/netty/issues/7159 - EventLoop loop = eventLoop(); - if (loop.inEventLoop()) { - doDeregister(); - } else { - loop.execute(new Runnable() { - @Override - public void run() { - try { - doDeregister(); - } catch (Throwable cause) { - pipeline().fireExceptionCaught(cause); - } - } - }); - } - } - } finally { - socket.close(); - } + socket.close(); } @Override From 5d448377e94ca1eca3ec994d34a1170912e57ae9 Mon Sep 17 00:00:00 2001 From: Dmitriy Dumanskiy Date: Mon, 25 Feb 2019 20:50:19 +0200 Subject: [PATCH 383/417] Avoid unnecessary char casts for CookieEncoder (#8827) Motivation: Avoid unnecessary (char) casts by changing variables types. Modifications: Use chars directly. Result: Less casts. --- .../handler/codec/http/cookie/CookieUtil.java | 26 +++++++++---------- .../http/cookie/ServerCookieEncoder.java | 6 ++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieUtil.java index 1e9d9c8f87e2..2e818b92eb39 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieUtil.java @@ -97,24 +97,24 @@ static String stripTrailingSeparator(StringBuilder buf) { static void add(StringBuilder sb, String name, long val) { sb.append(name); - sb.append((char) HttpConstants.EQUALS); + sb.append('='); sb.append(val); - sb.append((char) HttpConstants.SEMICOLON); - sb.append((char) HttpConstants.SP); + sb.append(';'); + sb.append(HttpConstants.SP_CHAR); } static void add(StringBuilder sb, String name, String val) { sb.append(name); - sb.append((char) HttpConstants.EQUALS); + sb.append('='); sb.append(val); - sb.append((char) HttpConstants.SEMICOLON); - sb.append((char) HttpConstants.SP); + sb.append(';'); + sb.append(HttpConstants.SP_CHAR); } static void add(StringBuilder sb, String name) { sb.append(name); - sb.append((char) HttpConstants.SEMICOLON); - sb.append((char) HttpConstants.SP); + sb.append(';'); + sb.append(HttpConstants.SP_CHAR); } static void addQuoted(StringBuilder sb, String name, String val) { @@ -123,12 +123,12 @@ static void addQuoted(StringBuilder sb, String name, String val) { } sb.append(name); - sb.append((char) HttpConstants.EQUALS); - sb.append((char) HttpConstants.DOUBLE_QUOTE); + sb.append('='); + sb.append('"'); sb.append(val); - sb.append((char) HttpConstants.DOUBLE_QUOTE); - sb.append((char) HttpConstants.SEMICOLON); - sb.append((char) HttpConstants.SP); + sb.append('"'); + sb.append(';'); + sb.append(HttpConstants.SP_CHAR); } static int firstInvalidCookieNameOctet(CharSequence cs) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ServerCookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ServerCookieEncoder.java index b707dc33d180..c5a1d7d0e7d1 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ServerCookieEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ServerCookieEncoder.java @@ -105,10 +105,10 @@ public String encode(Cookie cookie) { add(buf, CookieHeaderNames.MAX_AGE, cookie.maxAge()); Date expires = new Date(cookie.maxAge() * 1000 + System.currentTimeMillis()); buf.append(CookieHeaderNames.EXPIRES); - buf.append((char) HttpConstants.EQUALS); + buf.append('='); DateFormatter.append(expires, buf); - buf.append((char) HttpConstants.SEMICOLON); - buf.append((char) HttpConstants.SP); + buf.append(';'); + buf.append(HttpConstants.SP_CHAR); } if (cookie.path() != null) { From 81e43d50880fbe007c27e897034fdfa0c844a88b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 26 Feb 2019 11:08:09 +0100 Subject: [PATCH 384/417] DefaultFileRegion.transferTo with invalid count may cause busy-spin (#8885) Motivation: `DefaultFileRegion.transferTo` will return 0 all the time when we request more data then the actual file size. This may result in a busy spin while processing the fileregion during writes. Modifications: - If we wrote 0 bytes check if the underlying file size is smaller then the requested count and if so throw an IOException - Add DefaultFileRegionTest - Add a test to the testsuite Result: Fixes https://github.com/netty/netty/issues/8868. --- .../socket/SocketFileRegionTest.java | 35 +++++ .../epoll/AbstractEpollStreamChannel.java | 6 +- .../kqueue/AbstractKQueueStreamChannel.java | 7 +- .../io/netty/channel/AbstractChannel.java | 4 + .../io/netty/channel/DefaultFileRegion.java | 18 +++ .../netty/channel/DefaultFileRegionTest.java | 120 ++++++++++++++++++ 6 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 transport/src/test/java/io/netty/channel/DefaultFileRegionTest.java diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java index 53deb6c6743a..881acd1bcff3 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java @@ -22,11 +22,13 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultFileRegion; import io.netty.channel.FileRegion; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.util.internal.PlatformDependent; +import org.hamcrest.CoreMatchers; import org.junit.Test; import java.io.File; @@ -73,6 +75,11 @@ public void testFileRegionVoidPromiseNotAutoRead() throws Throwable { run(); } + @Test + public void testFileRegionCountLargerThenFile() throws Throwable { + run(); + } + public void testFileRegion(ServerBootstrap sb, Bootstrap cb) throws Throwable { testFileRegion0(sb, cb, false, true, true); } @@ -93,6 +100,34 @@ public void testFileRegionVoidPromiseNotAutoRead(ServerBootstrap sb, Bootstrap c testFileRegion0(sb, cb, true, false, true); } + public void testFileRegionCountLargerThenFile(ServerBootstrap sb, Bootstrap cb) throws Throwable { + File file = File.createTempFile("netty-", ".tmp"); + file.deleteOnExit(); + + final FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.close(); + + sb.childHandler(new SimpleChannelInboundHandler() { + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) { + // Just drop the message. + } + }); + cb.handler(new ChannelInboundHandlerAdapter()); + + Channel sc = sb.bind().sync().channel(); + Channel cc = cb.connect(sc.localAddress()).sync().channel(); + + // Request file region which is bigger then the underlying file. + FileRegion region = new DefaultFileRegion( + new FileInputStream(file).getChannel(), 0, data.length + 1024); + + assertThat(cc.writeAndFlush(region).await().cause(), CoreMatchers.instanceOf(IOException.class)); + cc.close().sync(); + sc.close().sync(); + } + private static void testFileRegion0( ServerBootstrap sb, Bootstrap cb, boolean voidPromise, final boolean autoRead, boolean defaultFileRegion) throws Throwable { diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java index 8d993b48e74b..e2f2c88cb72e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java @@ -367,13 +367,13 @@ private int writeBytesMultiple( * */ private int writeDefaultFileRegion(ChannelOutboundBuffer in, DefaultFileRegion region) throws Exception { + final long offset = region.transferred(); final long regionCount = region.count(); - if (region.transferred() >= regionCount) { + if (offset >= regionCount) { in.remove(); return 0; } - final long offset = region.transferred(); final long flushedAmount = socket.sendFile(region, region.position(), offset, regionCount - offset); if (flushedAmount > 0) { in.progress(flushedAmount); @@ -381,6 +381,8 @@ private int writeDefaultFileRegion(ChannelOutboundBuffer in, DefaultFileRegion r in.remove(); } return 1; + } else if (flushedAmount == 0) { + validateFileRegion(region, offset); } return WRITE_STATUS_SNDBUF_FULL; } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueStreamChannel.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueStreamChannel.java index 7d604f150acb..4b7f4e06e5b2 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueStreamChannel.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/AbstractKQueueStreamChannel.java @@ -210,12 +210,13 @@ private int writeBytesMultiple( */ private int writeDefaultFileRegion(ChannelOutboundBuffer in, DefaultFileRegion region) throws Exception { final long regionCount = region.count(); - if (region.transferred() >= regionCount) { + final long offset = region.transferred(); + + if (offset >= regionCount) { in.remove(); return 0; } - final long offset = region.transferred(); final long flushedAmount = socket.sendFile(region, region.position(), offset, regionCount - offset); if (flushedAmount > 0) { in.progress(flushedAmount); @@ -223,6 +224,8 @@ private int writeDefaultFileRegion(ChannelOutboundBuffer in, DefaultFileRegion r in.remove(); } return 1; + } else if (flushedAmount == 0) { + validateFileRegion(region, offset); } return WRITE_STATUS_SNDBUF_FULL; } diff --git a/transport/src/main/java/io/netty/channel/AbstractChannel.java b/transport/src/main/java/io/netty/channel/AbstractChannel.java index 8f8fe2f787a2..fcd596563ef7 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannel.java @@ -1149,6 +1149,10 @@ protected Object filterOutboundMessage(Object msg) throws Exception { return msg; } + protected void validateFileRegion(DefaultFileRegion region, long position) throws IOException { + DefaultFileRegion.validate(region, position); + } + static final class CloseFuture extends DefaultChannelPromise { CloseFuture(AbstractChannel ch) { diff --git a/transport/src/main/java/io/netty/channel/DefaultFileRegion.java b/transport/src/main/java/io/netty/channel/DefaultFileRegion.java index 2435b20a313b..2f6bea95a1cf 100644 --- a/transport/src/main/java/io/netty/channel/DefaultFileRegion.java +++ b/transport/src/main/java/io/netty/channel/DefaultFileRegion.java @@ -139,6 +139,12 @@ public long transferTo(WritableByteChannel target, long position) throws IOExcep long written = file.transferTo(this.position + position, count, target); if (written > 0) { transferred += written; + } else if (written == 0) { + // If the amount of written data is 0 we need to check if the requested count is bigger then the + // actual file itself as it may have been truncated on disk. + // + // See https://github.com/netty/netty/issues/8868 + validate(this, position); } return written; } @@ -182,4 +188,16 @@ public FileRegion touch() { public FileRegion touch(Object hint) { return this; } + + static void validate(DefaultFileRegion region, long position) throws IOException { + // If the amount of written data is 0 we need to check if the requested count is bigger then the + // actual file itself as it may have been truncated on disk. + // + // See https://github.com/netty/netty/issues/8868 + long size = region.file.size(); + long count = region.count - position; + if (region.position + count + position > size) { + throw new IOException("Underlying file size " + size + " smaller then requested count " + region.count); + } + } } diff --git a/transport/src/test/java/io/netty/channel/DefaultFileRegionTest.java b/transport/src/test/java/io/netty/channel/DefaultFileRegionTest.java new file mode 100644 index 000000000000..e416bccba594 --- /dev/null +++ b/transport/src/test/java/io/netty/channel/DefaultFileRegionTest.java @@ -0,0 +1,120 @@ +/* + * Copyright 2019 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: + * + * http://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.channel; + +import io.netty.util.internal.PlatformDependent; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.Channels; +import java.nio.channels.WritableByteChannel; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class DefaultFileRegionTest { + + private static final byte[] data = new byte[1048576 * 10]; + + static { + PlatformDependent.threadLocalRandom().nextBytes(data); + } + + private static File newFile() throws IOException { + File file = File.createTempFile("netty-", ".tmp"); + file.deleteOnExit(); + + final FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.close(); + return file; + } + + @Test + public void testCreateFromFile() throws IOException { + File file = newFile(); + try { + testFileRegion(new DefaultFileRegion(file, 0, data.length)); + } finally { + file.delete(); + } + } + + @Test + public void testCreateFromFileChannel() throws IOException { + File file = newFile(); + RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); + try { + testFileRegion(new DefaultFileRegion(randomAccessFile.getChannel(), 0, data.length)); + } finally { + randomAccessFile.close(); + file.delete(); + } + } + + private static void testFileRegion(FileRegion region) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + WritableByteChannel channel = Channels.newChannel(outputStream); + + try { + assertEquals(data.length, region.count()); + assertEquals(0, region.transferred()); + assertEquals(data.length, region.transferTo(channel, 0)); + assertEquals(data.length, region.count()); + assertEquals(data.length, region.transferred()); + assertArrayEquals(data, outputStream.toByteArray()); + } finally { + channel.close(); + } + } + + @Test + public void testTruncated() throws IOException { + File file = newFile(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + WritableByteChannel channel = Channels.newChannel(outputStream); + RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); + + try { + FileRegion region = new DefaultFileRegion(randomAccessFile.getChannel(), 0, data.length); + + randomAccessFile.getChannel().truncate(data.length - 1024); + + assertEquals(data.length, region.count()); + assertEquals(0, region.transferred()); + + assertEquals(data.length - 1024, region.transferTo(channel, 0)); + assertEquals(data.length, region.count()); + assertEquals(data.length - 1024, region.transferred()); + try { + region.transferTo(channel, data.length - 1024); + fail(); + } catch (IOException expected) { + // expected + } + } finally { + channel.close(); + + randomAccessFile.close(); + file.delete(); + } + } +} From e4d4775a10ed7e958ccdae3e5494bb0bee890bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Andr=C3=A9=20Pearce?= Date: Tue, 26 Feb 2019 20:02:56 +0000 Subject: [PATCH 385/417] Support removal using values iterator. (#8866) Motivation: As ActiveMQ project using netty, we want to make use of this class, unfortunately the iterator on values(), seems to not support remove method, even so the delegated iterator does. Currently we have to clone and modify this class locally albeit a one line change is needed, it would be ideal if netty could allow remove, then removing the need to maintain a clone. Modifications: * remove throws UnsupportedOperationException, and instead call remove method on delegated iterator Result: Be able to call Iterator.remove() for the values. --- .../templates/io/netty/util/collection/KObjectHashMap.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/templates/io/netty/util/collection/KObjectHashMap.template b/common/src/main/templates/io/netty/util/collection/KObjectHashMap.template index d8aeb1b25c96..7db1ceb6c30f 100644 --- a/common/src/main/templates/io/netty/util/collection/KObjectHashMap.template +++ b/common/src/main/templates/io/netty/util/collection/KObjectHashMap.template @@ -236,7 +236,7 @@ public class @K@ObjectHashMap implements @K@ObjectMap { @Override public void remove() { - throw new UnsupportedOperationException(); + iter.remove(); } }; } From 215b61e8e249e8f3f573864a53530e46ca7b21f7 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 27 Feb 2019 12:06:13 +0100 Subject: [PATCH 386/417] Add test for Iterator.remove() on KObjectHashMap.values().iterator() (#8891) Motivation: https://github.com/netty/netty/pull/8866 added support for calling Iterator.remove() but did not add a testcase. Modifications: Add testcase to ensure removal works. Result: Better test-coverage. --- .../collection/KObjectHashMapTest.template | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/common/src/test/templates/io/netty/util/collection/KObjectHashMapTest.template b/common/src/test/templates/io/netty/util/collection/KObjectHashMapTest.template index 2be12a9ea81a..d4214f7fce6c 100644 --- a/common/src/test/templates/io/netty/util/collection/KObjectHashMapTest.template +++ b/common/src/test/templates/io/netty/util/collection/KObjectHashMapTest.template @@ -613,4 +613,34 @@ public class @K@ObjectHashMapTest { } assertTrue(map.isEmpty()); } + + @Test + public void valuesIteratorRemove() { + Value v1 = new Value("v1"); + Value v2 = new Value("v2"); + Value v3 = new Value("v3"); + map.put((@k@) 1, v1); + map.put((@k@) 2, v2); + map.put((@k@) 3, v3); + + Iterator it = map.values().iterator(); + + assertSame(v1, it.next()); + assertSame(v2, it.next()); + it.remove(); + + assertSame(v3, it.next()); + assertFalse(it.hasNext()); + + assertEquals(2, map.size()); + assertSame(v1, map.get((@k@) 1)); + assertNull(map.get((@k@) 2)); + assertSame(v3, map.get((@k@) 3)); + + it = map.values().iterator(); + + assertSame(v1, it.next()); + assertSame(v3, it.next()); + assertFalse(it.hasNext()); + } } From d3d0b6478b92e5ec48a6378e7c646cd99d8f9bde Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 28 Feb 2019 13:54:04 +0100 Subject: [PATCH 387/417] Update JDK12 and 13 to latest EA releases. (#8809) Motivation: We use outdated EA releases when building and testing with JDK 12 and 13. Modifications: - Update versions. - Add workaround for possible JDK12+ bug. Result: Use latest releases --- docker/docker-compose.centos-6.112.yaml | 2 +- docker/docker-compose.centos-6.113.yaml | 2 +- docker/docker-compose.centos-7.112.yaml | 2 +- docker/docker-compose.centos-7.113.yaml | 2 +- .../test/java/io/netty/handler/ssl/SSLEngineTest.java | 9 ++++++++- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docker/docker-compose.centos-6.112.yaml b/docker/docker-compose.centos-6.112.yaml index 08b54114a9bd..86a97a5ce824 100644 --- a/docker/docker-compose.centos-6.112.yaml +++ b/docker/docker-compose.centos-6.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "openjdk@1.12.0-27" + java_version : "openjdk@1.12.0" test: image: netty:centos-6-1.12 diff --git a/docker/docker-compose.centos-6.113.yaml b/docker/docker-compose.centos-6.113.yaml index c8260673b5d2..78cf6c82ba54 100644 --- a/docker/docker-compose.centos-6.113.yaml +++ b/docker/docker-compose.centos-6.113.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "6" - java_version : "openjdk@1.13.0-3" + java_version : "openjdk@1.13.0-9" test: image: netty:centos-6-1.13 diff --git a/docker/docker-compose.centos-7.112.yaml b/docker/docker-compose.centos-7.112.yaml index b85a1d6e5ad0..627e8d34a0e3 100644 --- a/docker/docker-compose.centos-7.112.yaml +++ b/docker/docker-compose.centos-7.112.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "openjdk@1.12.0-27" + java_version : "openjdk@1.12.0" test: image: netty:centos-7-1.12 diff --git a/docker/docker-compose.centos-7.113.yaml b/docker/docker-compose.centos-7.113.yaml index e49a045b1ddc..d05d70a15805 100644 --- a/docker/docker-compose.centos-7.113.yaml +++ b/docker/docker-compose.centos-7.113.yaml @@ -7,7 +7,7 @@ services: build: args: centos_version : "7" - java_version : "openjdk@1.13.0-3" + java_version : "openjdk@1.13.0-9" test: image: netty:centos-7-1.13 diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 1148a0a06fe0..37b2e3917634 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -2195,7 +2195,14 @@ public void testCloseNotifySequence() throws Exception { assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); // Need an UNWRAP to read the response of the close_notify - assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus()); + if (PlatformDependent.javaVersion() >= 12 && sslClientProvider() == SslProvider.JDK) { + // This is a workaround for a possible JDK12+ bug. + // + // See http://mail.openjdk.java.net/pipermail/security-dev/2019-February/019406.html. + assertEquals(SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, result.getHandshakeStatus()); + } else { + assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus()); + } int produced = result.bytesProduced(); int consumed = result.bytesConsumed(); From c6d3792df01210df05c98e1b4f8beb95be9f368e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 28 Feb 2019 20:29:40 +0100 Subject: [PATCH 388/417] Correctly resume wrap / unwrap when SslTask execution completes (#8899) Motivation: fa6a8cb09c9679468a6c2d912ddfbbe885ee0c08 introduced correct dispatching of delegated tasks for SSLEngine but did not correctly handle some cases for resuming wrap / unwrap after the task was executed. This could lead to stales, which showed up during tests when running with Java11 and BoringSSL. Modifications: - Correctly resume wrap / unwrap in all cases. - Fix timeout value which was changed in previous commit by mistake. Result: No more stales after task execution. --- .../java/io/netty/handler/ssl/SslHandler.java | 27 ++++++++++++++----- .../io/netty/handler/ssl/SSLEngineTest.java | 2 +- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index 367c891cf04a..6f327bcfc067 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -953,7 +953,8 @@ private boolean wrapNonAppData(ChannelHandlerContext ctx, boolean inUnwrap) thro out = null; } - switch (result.getHandshakeStatus()) { + HandshakeStatus status = result.getHandshakeStatus(); + switch (status) { case FINISHED: setHandshakeSuccess(); return false; @@ -988,7 +989,7 @@ private boolean wrapNonAppData(ChannelHandlerContext ctx, boolean inUnwrap) thro throw new IllegalStateException("Unknown handshake status: " + result.getHandshakeStatus()); } - if (result.bytesProduced() == 0) { + if (result.bytesProduced() == 0 && status != HandshakeStatus.NEED_TASK) { break; } @@ -1438,7 +1439,9 @@ private int unwrap( throw new IllegalStateException("unknown handshake status: " + handshakeStatus); } - if (status == Status.BUFFER_UNDERFLOW || consumed == 0 && produced == 0) { + if (status == Status.BUFFER_UNDERFLOW || + // If we processed NEED_TASK we should try again even we did not consume or produce anything. + handshakeStatus != HandshakeStatus.NEED_TASK && consumed == 0 && produced == 0) { if (handshakeStatus == HandshakeStatus.NEED_UNWRAP) { // The underlying engine is starving so we need to feed it with more data. // See https://github.com/netty/netty/pull/5039 @@ -1624,6 +1627,11 @@ private void resumeOnEventExecutor() { taskError(e); return; } + if (inUnwrap) { + // If we were in the unwrap call when the task was processed we should also try to unwrap + // non app data first as there may not anything left in the inbound buffer to process. + unwrapNonAppData(ctx); + } // Flush now as we may have written some data as part of the wrap call. forceFlush(ctx); @@ -1642,13 +1650,20 @@ private void resumeOnEventExecutor() { // that will be written to the Channel. case NEED_WRAP: try { - wrapNonAppData(ctx, inUnwrap); + if (!wrapNonAppData(ctx, false) && inUnwrap) { + // The handshake finished in wrapNonAppData(...), we need to try call + // unwrapNonAppData(...) as we may have some alert that we should read. + // + // This mimics what we would do when we are calling this method while in unwrap(...). + unwrapNonAppData(ctx); + } + + // Flush now as we may have written some data as part of the wrap call. + forceFlush(ctx); } catch (Throwable e) { taskError(e); return; } - // Flush now as we may have written some data as part of the wrap call. - forceFlush(ctx); // Now try to feed in more data that we have buffered. tryDecodeAgain(); diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 37b2e3917634..51b02aa18579 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -1151,7 +1151,7 @@ private static void writeAndVerifyReceived(ByteBuf message, Channel sendChannel, MessageReceiver receiver) throws Exception { List dataCapture = null; try { - assertTrue(sendChannel.writeAndFlush(message).await(50, TimeUnit.SECONDS)); + assertTrue(sendChannel.writeAndFlush(message).await(5, TimeUnit.SECONDS)); receiverLatch.await(5, TimeUnit.SECONDS); message.resetReaderIndex(); ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); From 625c4e8286d3c8a36046feb1289b6b710c7541a5 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 28 Feb 2019 20:32:04 +0100 Subject: [PATCH 389/417] Tighten up contract of PromiseCombiner and so make it more safe to use (#8886) Motivation: PromiseCombiner is not thread-safe and even assumes all added Futures are using the same EventExecutor. This is kind of fragile as we do not enforce this. We need to enforce this contract to ensure it's safe to use and easy to spot concurrency problems. Modifications: - Add new contructor to PromiseCombiner that takes an EventExecutor and deprecate the old non-arg constructor. - Check if methods are called from within the EventExecutor thread and if not fail - Correctly dispatch on the right EventExecutor if the Future uses a different EventExecutor to eliminate concurrency issues. Result: More safe use of PromiseCombiner + enforce correct usage / contract. --- .../CompressorHttp2ConnectionEncoder.java | 2 +- .../codec/MessageToMessageEncoder.java | 2 +- .../util/concurrent/PromiseAggregator.java | 2 +- .../util/concurrent/PromiseCombiner.java | 55 +++++++++++++++++-- .../util/concurrent/PromiseCombinerTest.java | 36 +++++++++++- .../io/netty/channel/PendingWriteQueue.java | 2 +- 6 files changed, 88 insertions(+), 11 deletions(-) diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java index 3137da21ed25..d0ba8646c734 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java @@ -108,7 +108,7 @@ public ChannelFuture writeData(final ChannelHandlerContext ctx, final int stream return promise; } - PromiseCombiner combiner = new PromiseCombiner(); + PromiseCombiner combiner = new PromiseCombiner(ctx.executor()); for (;;) { ByteBuf nextBuf = nextReadableBuf(channel); boolean compressedEndOfStream = nextBuf == null && endOfStream; diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java index 6b47b6f8a2ae..439dc8cb193e 100644 --- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java @@ -132,7 +132,7 @@ private static void writeVoidPromise(ChannelHandlerContext ctx, CodecOutputList } private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, ChannelPromise promise) { - final PromiseCombiner combiner = new PromiseCombiner(); + final PromiseCombiner combiner = new PromiseCombiner(ctx.executor()); for (int i = 0; i < out.size(); i++) { combiner.add(ctx.write(out.getUnsafe(i))); } diff --git a/common/src/main/java/io/netty/util/concurrent/PromiseAggregator.java b/common/src/main/java/io/netty/util/concurrent/PromiseAggregator.java index 7d908c037949..215d70923280 100644 --- a/common/src/main/java/io/netty/util/concurrent/PromiseAggregator.java +++ b/common/src/main/java/io/netty/util/concurrent/PromiseAggregator.java @@ -20,7 +20,7 @@ import java.util.Set; /** - * @deprecated Use {@link PromiseCombiner} + * @deprecated Use {@link PromiseCombiner#PromiseCombiner(EventExecutor)}. * * {@link GenericFutureListener} implementation which consolidates multiple {@link Future}s * into one, by listening to individual {@link Future}s and producing an aggregated result diff --git a/common/src/main/java/io/netty/util/concurrent/PromiseCombiner.java b/common/src/main/java/io/netty/util/concurrent/PromiseCombiner.java index 38b9de14cbc2..8895c1a0e8e0 100644 --- a/common/src/main/java/io/netty/util/concurrent/PromiseCombiner.java +++ b/common/src/main/java/io/netty/util/concurrent/PromiseCombiner.java @@ -28,26 +28,62 @@ * {@link PromiseCombiner#add(Future)} and {@link PromiseCombiner#addAll(Future[])} methods. When all futures to be * combined have been added, callers must provide an aggregate promise to be notified when all combined promises have * finished via the {@link PromiseCombiner#finish(Promise)} method.

    + * + *

    This implementation is NOT thread-safe and all methods must be called + * from the {@link EventExecutor} thread.

    */ public final class PromiseCombiner { private int expectedCount; private int doneCount; - private boolean doneAdding; private Promise aggregatePromise; private Throwable cause; private final GenericFutureListener> listener = new GenericFutureListener>() { @Override - public void operationComplete(Future future) throws Exception { + public void operationComplete(final Future future) { + if (executor.inEventLoop()) { + operationComplete0(future); + } else { + executor.execute(new Runnable() { + @Override + public void run() { + operationComplete0(future); + } + }); + } + } + + private void operationComplete0(Future future) { + assert executor.inEventLoop(); ++doneCount; if (!future.isSuccess() && cause == null) { cause = future.cause(); } - if (doneCount == expectedCount && doneAdding) { + if (doneCount == expectedCount && aggregatePromise != null) { tryPromise(); } } }; + 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[])} + * and {@link #finish(Promise)} from within the {@link EventExecutor} thread. + * + * @param executor the {@link EventExecutor} to use for notifications. + */ + public PromiseCombiner(EventExecutor executor) { + this.executor = ObjectUtil.checkNotNull(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. @@ -70,6 +106,7 @@ public void add(Promise promise) { @SuppressWarnings({ "unchecked", "rawtypes" }) public void add(Future future) { checkAddAllowed(); + checkInEventLoop(); ++expectedCount; future.addListener(listener); } @@ -113,22 +150,28 @@ public void addAll(Future... futures) { */ public void finish(Promise aggregatePromise) { ObjectUtil.checkNotNull(aggregatePromise, "aggregatePromise"); - if (doneAdding) { + checkInEventLoop(); + if (this.aggregatePromise != null) { throw new IllegalStateException("Already finished"); } - doneAdding = true; this.aggregatePromise = aggregatePromise; if (doneCount == expectedCount) { tryPromise(); } } + private void checkInEventLoop() { + if (!executor.inEventLoop()) { + throw new IllegalStateException("Must be called from EventExecutor thread"); + } + } + private boolean tryPromise() { return (cause == null) ? aggregatePromise.trySuccess(null) : aggregatePromise.tryFailure(cause); } private void checkAddAllowed() { - if (doneAdding) { + if (aggregatePromise != null) { throw new IllegalStateException("Adding promises is not allowed after finished adding"); } } diff --git a/common/src/test/java/io/netty/util/concurrent/PromiseCombinerTest.java b/common/src/test/java/io/netty/util/concurrent/PromiseCombinerTest.java index d77aab70ce49..c1ec93784a84 100644 --- a/common/src/test/java/io/netty/util/concurrent/PromiseCombinerTest.java +++ b/common/src/test/java/io/netty/util/concurrent/PromiseCombinerTest.java @@ -19,6 +19,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -26,6 +27,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -56,7 +58,7 @@ public void accept(GenericFutureListener> listener) { @Before public void setup() { MockitoAnnotations.initMocks(this); - combiner = new PromiseCombiner(); + combiner = new PromiseCombiner(ImmediateEventExecutor.INSTANCE); } @Test @@ -161,6 +163,38 @@ public void testAddFail() throws Exception { verifyFail(p3, e1); } + @Test + public void testEventExecutor() { + EventExecutor executor = mock(EventExecutor.class); + when(executor.inEventLoop()).thenReturn(false); + combiner = new PromiseCombiner(executor); + + Future future = mock(Future.class); + + try { + combiner.add(future); + Assert.fail(); + } catch (IllegalStateException expected) { + // expected + } + + try { + combiner.addAll(future); + Assert.fail(); + } catch (IllegalStateException expected) { + // expected + } + + @SuppressWarnings("unchecked") + Promise promise = (Promise) mock(Promise.class); + try { + combiner.finish(promise); + Assert.fail(); + } catch (IllegalStateException expected) { + // expected + } + } + private static void verifyFail(Promise p, Throwable cause) { verify(p).tryFailure(eq(cause)); } diff --git a/transport/src/main/java/io/netty/channel/PendingWriteQueue.java b/transport/src/main/java/io/netty/channel/PendingWriteQueue.java index 16ae47bd24a8..bbc661bf6c80 100644 --- a/transport/src/main/java/io/netty/channel/PendingWriteQueue.java +++ b/transport/src/main/java/io/netty/channel/PendingWriteQueue.java @@ -130,7 +130,7 @@ public ChannelFuture removeAndWriteAll() { } ChannelPromise p = ctx.newPromise(); - PromiseCombiner combiner = new PromiseCombiner(); + PromiseCombiner combiner = new PromiseCombiner(ctx.executor()); try { // It is possible for some of the written promises to trigger more writes. The new writes // will "revive" the queue, so we need to write them up until the queue is empty. From 0811409ca3f4a8df94c9d2e921c47c7487e90793 Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Thu, 28 Feb 2019 11:40:42 -0800 Subject: [PATCH 390/417] Further reduce ensureAccessible() overhead (#8895) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: This PR fixes some non-negligible overhead discovered in the ByteBuf accessibility (non-zero refcount) checking. The cause turned out to be mostly twofold: - Unnecessary operations used to calculate the refcount from the "raw" encoded int field value - Call stack depths exceeding the default limit for inlining, in some places (CompositeByteBuf in particular) It's a follow-on from #8882 which uses the maxCapacity field for a simpler non-negative check. The performance gap between these two variants appears to be _mostly_ closed, but there's one exception which may warrant further analysis. Modifications: - Replace ABB.internalRefCount() with ByteBuf.isAccessible(), the default still checks for non-zero refCnt() - Just test for parity of raw refCnt instead of converting to "real", with fast-path for specific small values - Make sure isAccessible() is delegated by derived/wrapper ByteBufs - Use existing freed flag in CompositeByteBuf for faster isAccessible() - Manually inline some calls in methods like CompositeByteBuf.setLong() and AbstractReferenceCountedByteBuf.isAccessible() to reduce stack depths (to ensure default inlining limit isn't hit) - Add ByteBufAccessBenchmark which is an extension of UnsafeByteBufBenchmark (maybe latter could now be removed) Results: Before: Benchmark (bufferType) (checkAccessible) (checkBounds) Mode Cnt Score Error Units readBatch UNSAFE true true thrpt 30 84524972.863 ± 518338.811 ops/s readBatch UNSAFE_SLICE true true thrpt 30 38608795.037 ± 298176.974 ops/s readBatch HEAP true true thrpt 30 80003697.649 ± 974674.119 ops/s readBatch COMPOSITE true true thrpt 30 18495554.788 ± 108075.023 ops/s setGetLong UNSAFE true true thrpt 30 247069881.578 ± 10839162.593 ops/s setGetLong UNSAFE_SLICE true true thrpt 30 196355905.206 ± 1802420.990 ops/s setGetLong HEAP true true thrpt 30 245686644.713 ± 11769311.527 ops/s setGetLong COMPOSITE true true thrpt 30 83170940.687 ± 657524.123 ops/s setLong UNSAFE true true thrpt 30 278940253.918 ± 1807265.259 ops/s setLong UNSAFE_SLICE true true thrpt 30 202556738.764 ± 11887973.563 ops/s setLong HEAP true true thrpt 30 280045958.053 ± 2719583.400 ops/s setLong COMPOSITE true true thrpt 30 121299806.002 ± 2155084.707 ops/s After: Benchmark (bufferType) (checkAccessible) (checkBounds) Mode Cnt Score Error Units readBatch UNSAFE true true thrpt 30 101641801.035 ± 3950050.059 ops/s readBatch UNSAFE_SLICE true true thrpt 30 84395902.846 ± 4339579.057 ops/s readBatch HEAP true true thrpt 30 100179060.207 ± 3222487.287 ops/s readBatch COMPOSITE true true thrpt 30 42288494.472 ± 294919.633 ops/s setGetLong UNSAFE true true thrpt 30 304530755.027 ± 6574163.899 ops/s setGetLong UNSAFE_SLICE true true thrpt 30 212028547.645 ± 14277828.768 ops/s setGetLong HEAP true true thrpt 30 309335422.609 ± 2272150.415 ops/s setGetLong COMPOSITE true true thrpt 30 160383609.236 ± 966484.033 ops/s setLong UNSAFE true true thrpt 30 298055969.747 ± 7437449.627 ops/s setLong UNSAFE_SLICE true true thrpt 30 223784178.650 ± 9869750.095 ops/s setLong HEAP true true thrpt 30 302543263.328 ± 8140104.706 ops/s setLong COMPOSITE true true thrpt 30 157083673.285 ± 3528779.522 ops/s There's also a similar knock-on improvement to other benchmarks (e.g. HPACK encoding/decoding) as shown in #8882. For sanity I did a final comparison of the "fast path" tweak using one of the HPACK benchmarks: (rawCnt & 1) == 0: Benchmark (limitToAscii) (sensitive) (size) Mode Cnt Score Error Units HpackDecoderBenchmark.decode true true MEDIUM thrpt 30 50914.479 ± 940.114 ops/s rawCnt == 2 || rawCnt == 4 || rawCnt == 6 || rawCnt == 8 || (rawCnt & 1) == 0: Benchmark (limitToAscii) (sensitive) (size) Mode Cnt Score Error Units HpackDecoderBenchmark.decode true true MEDIUM thrpt 30 60036.425 ± 1478.196 ops/s --- .../java/io/netty/buffer/AbstractByteBuf.java | 10 +- .../netty/buffer/AbstractDerivedByteBuf.java | 5 + .../AbstractReferenceCountedByteBuf.java | 13 +- .../main/java/io/netty/buffer/ByteBuf.java | 8 + .../io/netty/buffer/CompositeByteBuf.java | 22 ++- .../java/io/netty/buffer/SwappedByteBuf.java | 5 + .../java/io/netty/buffer/WrappedByteBuf.java | 5 + .../netty/buffer/WrappedCompositeByteBuf.java | 4 +- .../netty/buffer/ByteBufAccessBenchmark.java | 157 ++++++++++++++++++ 9 files changed, 209 insertions(+), 20 deletions(-) create mode 100644 microbench/src/main/java/io/netty/buffer/ByteBufAccessBenchmark.java diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index aa5e5e3af128..ad2f793b1abf 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -1437,19 +1437,11 @@ private void checkReadableBytes0(int minimumReadableBytes) { * if the buffer was released before. */ protected final void ensureAccessible() { - if (checkAccessible && internalRefCnt() == 0) { + if (checkAccessible && !isAccessible()) { throw new IllegalReferenceCountException(0); } } - /** - * Returns the reference count that is used internally by {@link #ensureAccessible()} to try to guard - * against using the buffer after it was released (best-effort). - */ - int internalRefCnt() { - return refCnt(); - } - final void setIndex0(int readerIndex, int writerIndex) { this.readerIndex = readerIndex; this.writerIndex = writerIndex; diff --git a/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java index 58f1d907a156..40844020dd21 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java @@ -31,6 +31,11 @@ protected AbstractDerivedByteBuf(int maxCapacity) { super(maxCapacity); } + @Override + final boolean isAccessible() { + return unwrap().isAccessible(); + } + @Override public final int refCnt() { return refCnt0(); diff --git a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java index ba26b1921a6b..548bdba5e493 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java @@ -64,10 +64,19 @@ private int nonVolatileRawCnt() { } @Override - int internalRefCnt() { + boolean isAccessible() { // Try to do non-volatile read for performance as the ensureAccessible() is racy anyway and only provide // a best-effort guard. - return realRefCnt(nonVolatileRawCnt()); + + // This is copied explicitly from the nonVolatileRawCnt() method above to reduce call stack depth, + // to avoid hitting the default limit for inlining (9) + final int rawCnt = REFCNT_FIELD_OFFSET != -1 ? PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET) + : refCntUpdater.get(this); + + // The "real" ref count is > 0 if the rawCnt is even. + // (x & y) appears to be surprisingly expensive relative to (x == y). Thus the expression below provides + // a fast path for most common cases where the ref count is 1, 2, 3 or 4. + return rawCnt == 2 || rawCnt == 4 || rawCnt == 6 || rawCnt == 8 || (rawCnt & 1) == 0; } @Override diff --git a/buffer/src/main/java/io/netty/buffer/ByteBuf.java b/buffer/src/main/java/io/netty/buffer/ByteBuf.java index 2ac7e988c6bc..83ecc6685a5b 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBuf.java @@ -2445,4 +2445,12 @@ public ByteBuf writeDoubleLE(double value) { @Override public abstract ByteBuf touch(Object hint); + + /** + * Used internally by {@link AbstractByteBuf#ensureAccessible()} to try to guard + * against using the buffer after it was released (best-effort). + */ + boolean isAccessible() { + return refCnt() != 0; + } } diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index ab4cb577b973..dbd69a163b31 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -300,7 +300,7 @@ private int addComponent0(boolean increaseWriterIndex, int cIndex, ByteBuf buffe @SuppressWarnings("deprecation") private Component newComponent(ByteBuf buf, int offset) { - if (checkAccessible && buf.refCnt() == 0) { + if (checkAccessible && !buf.isAccessible()) { throw new IllegalReferenceCountException(0); } int srcIndex = buf.readerIndex(), len = buf.readableBytes(); @@ -1074,7 +1074,8 @@ protected void _setByte(int index, int value) { @Override public CompositeByteBuf setShort(int index, int value) { - super.setShort(index, value); + checkIndex(index, 2); + _setShort(index, value); return this; } @@ -1108,7 +1109,8 @@ protected void _setShortLE(int index, int value) { @Override public CompositeByteBuf setMedium(int index, int value) { - super.setMedium(index, value); + checkIndex(index, 3); + _setMedium(index, value); return this; } @@ -1142,7 +1144,8 @@ protected void _setMediumLE(int index, int value) { @Override public CompositeByteBuf setInt(int index, int value) { - super.setInt(index, value); + checkIndex(index, 4); + _setInt(index, value); return this; } @@ -1176,7 +1179,8 @@ protected void _setIntLE(int index, int value) { @Override public CompositeByteBuf setLong(int index, long value) { - super.setLong(index, value); + checkIndex(index, 8); + _setLong(index, value); return this; } @@ -1284,7 +1288,6 @@ public int setBytes(int index, InputStream in, int length) throws IOException { int i = toComponentIndex0(index); int readBytes = 0; - do { Component c = components[i]; int localLength = Math.min(length, c.endOffset - index); @@ -2052,7 +2055,7 @@ public CompositeByteBuf writeBytes(ByteBuf src, int srcIndex, int length) { @Override public CompositeByteBuf writeBytes(byte[] src) { - writeBytes(src, 0, src.length); + super.writeBytes(src, 0, src.length); return this; } @@ -2120,6 +2123,11 @@ protected void deallocate() { } } + @Override + boolean isAccessible() { + return !freed; + } + @Override public ByteBuf unwrap() { return null; diff --git a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java index 5d54a1f9f14c..abf27663f7a9 100644 --- a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java @@ -997,6 +997,11 @@ public int refCnt() { return buf.refCnt(); } + @Override + final boolean isAccessible() { + return buf.isAccessible(); + } + @Override public ByteBuf retain() { buf.retain(); diff --git a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java index 45aa60ce889b..33570e200447 100644 --- a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java @@ -1033,4 +1033,9 @@ public boolean release() { public boolean release(int decrement) { return buf.release(decrement); } + + @Override + final boolean isAccessible() { + return buf.isAccessible(); + } } diff --git a/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java index b124eb2d2b96..c300ea087e25 100644 --- a/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java @@ -424,8 +424,8 @@ public final int refCnt() { } @Override - int internalRefCnt() { - return wrapped.internalRefCnt(); + final boolean isAccessible() { + return wrapped.isAccessible(); } @Override diff --git a/microbench/src/main/java/io/netty/buffer/ByteBufAccessBenchmark.java b/microbench/src/main/java/io/netty/buffer/ByteBufAccessBenchmark.java new file mode 100644 index 000000000000..c7cad7ba0744 --- /dev/null +++ b/microbench/src/main/java/io/netty/buffer/ByteBufAccessBenchmark.java @@ -0,0 +1,157 @@ +/* +* Copyright 2019 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: +* +* http://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.buffer; + +import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import io.netty.microbench.util.AbstractMicrobenchmark; +import io.netty.util.internal.PlatformDependent; + +@Warmup(iterations = 5, time = 1500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 1500, timeUnit = TimeUnit.MILLISECONDS) +@Fork(3) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class ByteBufAccessBenchmark extends AbstractMicrobenchmark { + + static final class NioFacade extends WrappedByteBuf { + private final ByteBuffer byteBuffer; + NioFacade(ByteBuffer byteBuffer) { + super(Unpooled.EMPTY_BUFFER); + this.byteBuffer = byteBuffer; + } + @Override + public ByteBuf setLong(int index, long value) { + byteBuffer.putLong(index, value); + return this; + } + @Override + public long getLong(int index) { + return byteBuffer.getLong(index); + } + @Override + public byte readByte() { + return byteBuffer.get(); + } + @Override + public ByteBuf touch() { + // hack since WrappedByteBuf.readerIndex(int) is final + byteBuffer.position(0); + return this; + } + @Override + public boolean release() { + PlatformDependent.freeDirectBuffer(byteBuffer); + return true; + } + } + + public enum ByteBufType { + UNSAFE { + @Override + ByteBuf newBuffer() { + return new UnpooledUnsafeDirectByteBuf( + UnpooledByteBufAllocator.DEFAULT, 64, 64).setIndex(0, 64); + } + }, + UNSAFE_SLICE { + @Override + ByteBuf newBuffer() { + return UNSAFE.newBuffer().slice(16, 48); + } + }, + HEAP { + @Override + ByteBuf newBuffer() { + return new UnpooledUnsafeHeapByteBuf( + UnpooledByteBufAllocator.DEFAULT, 64, 64).setIndex(0, 64); + } + }, + COMPOSITE { + @Override + ByteBuf newBuffer() { + return Unpooled.wrappedBuffer(UNSAFE.newBuffer(), HEAP.newBuffer()); + } + }, + NIO { + @Override + ByteBuf newBuffer() { + return new NioFacade(ByteBuffer.allocateDirect(64)); + } + }; + abstract ByteBuf newBuffer(); + } + + @Param + public ByteBufType bufferType; + + @Param({ "true", "false" }) + public String checkAccessible; + + @Param({ "true", "false" }) + public String checkBounds; + + @Param({ "8" }) + public int batchSize; // applies only to readBatch benchmark + + @Setup + public void setup() { + System.setProperty("io.netty.buffer.checkAccessible", checkAccessible); + System.setProperty("io.netty.buffer.checkBounds", checkBounds); + buffer = bufferType.newBuffer(); + } + + private ByteBuf buffer; + + @TearDown + public void tearDown() { + buffer.release(); + System.clearProperty("io.netty.buffer.checkAccessible"); + System.clearProperty("io.netty.buffer.checkBounds"); + } + + @Benchmark + public long setGetLong() { + return buffer.setLong(0, 1).getLong(0); + } + + @Benchmark + public ByteBuf setLong() { + return buffer.setLong(0, 1); + } + + @Benchmark + public int readBatch() { + buffer.readerIndex(0).touch(); + int result = 0; + for (int i = 0, size = batchSize; i < size; i++) { + result += buffer.readByte(); + } + return result; + } +} From e609b5eeb7daba819f530fea500991c9cfc18412 Mon Sep 17 00:00:00 2001 From: Konstantin Lutovich Date: Thu, 28 Feb 2019 21:13:56 +0100 Subject: [PATCH 391/417] Close consumed inputs in ChunkedWriteHandler (#8876) Motivation: ChunkedWriteHandler needs to close both successful and failed ChunkInputs. It used to never close successful ones. Modifications: * ChunkedWriteHandler always closes ChunkInput before completing the write promise. * Ensure only ChunkInput#close() is invoked on a failed input. * Ensure no methods are invoked on a closed input. Result: Fixes https://github.com/netty/netty/issues/8875. --- .../handler/stream/ChunkedWriteHandler.java | 40 +-- .../stream/ChunkedWriteHandlerTest.java | 232 +++++++++++++++++- 2 files changed, 256 insertions(+), 16 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java index f39328dff7a2..1a1822b59735 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java @@ -166,22 +166,28 @@ private void discard(Throwable cause) { Object message = currentWrite.msg; if (message instanceof ChunkedInput) { ChunkedInput in = (ChunkedInput) message; + boolean endOfInput; + long inputLength; try { - if (!in.isEndOfInput()) { - if (cause == null) { - cause = new ClosedChannelException(); - } - currentWrite.fail(cause); - } else { - currentWrite.success(in.length()); - } + endOfInput = in.isEndOfInput(); + inputLength = in.length(); closeInput(in); } catch (Exception e) { + closeInput(in); currentWrite.fail(e); if (logger.isWarnEnabled()) { - logger.warn(ChunkedInput.class.getSimpleName() + ".isEndOfInput() failed", e); + logger.warn(ChunkedInput.class.getSimpleName() + " failed", e); } - closeInput(in); + continue; + } + + if (!endOfInput) { + if (cause == null) { + cause = new ClosedChannelException(); + } + currentWrite.fail(cause); + } else { + currentWrite.success(inputLength); } } else { if (cause == null) { @@ -249,8 +255,8 @@ private void doFlush(final ChannelHandlerContext ctx) { ReferenceCountUtil.release(message); } - currentWrite.fail(t); closeInput(chunks); + currentWrite.fail(t); break; } @@ -283,8 +289,12 @@ public void operationComplete(ChannelFuture future) throws Exception { closeInput(chunks); currentWrite.fail(future.cause()); } else { - currentWrite.progress(chunks.progress(), chunks.length()); - currentWrite.success(chunks.length()); + // read state of the input in local variables before closing it + long inputProgress = chunks.progress(); + long inputLength = chunks.length(); + closeInput(chunks); + currentWrite.progress(inputProgress, inputLength); + currentWrite.success(inputLength); } } }); @@ -293,7 +303,7 @@ public void operationComplete(ChannelFuture future) throws Exception { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { - closeInput((ChunkedInput) pendingMessage); + closeInput(chunks); currentWrite.fail(future.cause()); } else { currentWrite.progress(chunks.progress(), chunks.length()); @@ -305,7 +315,7 @@ public void operationComplete(ChannelFuture future) throws Exception { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { - closeInput((ChunkedInput) pendingMessage); + closeInput(chunks); currentWrite.fail(future.cause()); } else { currentWrite.progress(chunks.progress(), chunks.length()); diff --git a/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java b/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java index 5b03048ba697..be6951d88bdb 100644 --- a/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java @@ -21,8 +21,8 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.CharsetUtil; import io.netty.util.ReferenceCountUtil; @@ -33,9 +33,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.Channels; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import static java.util.concurrent.TimeUnit.*; import static org.junit.Assert.*; public class ChunkedWriteHandlerTest { @@ -433,6 +435,142 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) assertEquals(1, chunks.get()); } + @Test + public void testCloseSuccessfulChunkedInput() { + int chunks = 10; + TestChunkedInput input = new TestChunkedInput(chunks); + EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); + + assertTrue(ch.writeOutbound(input)); + + for (int i = 0; i < chunks; i++) { + ByteBuf buf = ch.readOutbound(); + assertEquals(i, buf.readInt()); + buf.release(); + } + + assertTrue(input.isClosed()); + assertFalse(ch.finish()); + } + + @Test + public void testCloseFailedChunkedInput() { + Exception error = new Exception("Unable to produce a chunk"); + ThrowingChunkedInput input = new ThrowingChunkedInput(error); + + EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); + + try { + ch.writeOutbound(input); + fail("Exception expected"); + } catch (Exception e) { + assertEquals(error, e); + } + + assertTrue(input.isClosed()); + assertFalse(ch.finish()); + } + + @Test + public void testWriteListenerInvokedAfterSuccessfulChunkedInputClosed() throws Exception { + final TestChunkedInput input = new TestChunkedInput(2); + EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); + + final AtomicBoolean inputClosedWhenListenerInvoked = new AtomicBoolean(); + final CountDownLatch listenerInvoked = new CountDownLatch(1); + + ChannelFuture writeFuture = ch.write(input); + writeFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) { + inputClosedWhenListenerInvoked.set(input.isClosed()); + listenerInvoked.countDown(); + } + }); + ch.flush(); + + assertTrue(listenerInvoked.await(10, SECONDS)); + assertTrue(writeFuture.isSuccess()); + assertTrue(inputClosedWhenListenerInvoked.get()); + assertTrue(ch.finishAndReleaseAll()); + } + + @Test + public void testWriteListenerInvokedAfterFailedChunkedInputClosed() throws Exception { + final ThrowingChunkedInput input = new ThrowingChunkedInput(new RuntimeException()); + EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); + + final AtomicBoolean inputClosedWhenListenerInvoked = new AtomicBoolean(); + final CountDownLatch listenerInvoked = new CountDownLatch(1); + + ChannelFuture writeFuture = ch.write(input); + writeFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) { + inputClosedWhenListenerInvoked.set(input.isClosed()); + listenerInvoked.countDown(); + } + }); + ch.flush(); + + assertTrue(listenerInvoked.await(10, SECONDS)); + assertFalse(writeFuture.isSuccess()); + assertTrue(inputClosedWhenListenerInvoked.get()); + assertFalse(ch.finish()); + } + + @Test + public void testWriteListenerInvokedAfterChannelClosedAndInputFullyConsumed() throws Exception { + // use empty input which has endOfInput = true + final TestChunkedInput input = new TestChunkedInput(0); + EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); + + final AtomicBoolean inputClosedWhenListenerInvoked = new AtomicBoolean(); + final CountDownLatch listenerInvoked = new CountDownLatch(1); + + ChannelFuture writeFuture = ch.write(input); + writeFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) { + inputClosedWhenListenerInvoked.set(input.isClosed()); + listenerInvoked.countDown(); + } + }); + ch.close(); // close channel to make handler discard the input on subsequent flush + ch.flush(); + + assertTrue(listenerInvoked.await(10, SECONDS)); + assertTrue(writeFuture.isSuccess()); + assertTrue(inputClosedWhenListenerInvoked.get()); + assertFalse(ch.finish()); + } + + @Test + public void testWriteListenerInvokedAfterChannelClosedAndInputNotFullyConsumed() throws Exception { + // use non-empty input which has endOfInput = false + final TestChunkedInput input = new TestChunkedInput(42); + EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); + + final AtomicBoolean inputClosedWhenListenerInvoked = new AtomicBoolean(); + final CountDownLatch listenerInvoked = new CountDownLatch(1); + + ChannelFuture writeFuture = ch.write(input); + writeFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) { + inputClosedWhenListenerInvoked.set(input.isClosed()); + listenerInvoked.countDown(); + } + }); + ch.close(); // close channel to make handler discard the input on subsequent flush + ch.flush(); + + assertTrue(listenerInvoked.await(10, SECONDS)); + assertFalse(writeFuture.isSuccess()); + assertTrue(inputClosedWhenListenerInvoked.get()); + assertFalse(ch.finish()); + } + private static void check(Object... inputs) { EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); @@ -524,4 +662,96 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) assertEquals(BYTES.length, read); } + + private static final class TestChunkedInput implements ChunkedInput { + private final int chunksToProduce; + + private int chunksProduced; + private volatile boolean closed; + + TestChunkedInput(int chunksToProduce) { + this.chunksToProduce = chunksToProduce; + } + + @Override + public boolean isEndOfInput() { + return chunksProduced >= chunksToProduce; + } + + @Override + public void close() { + closed = true; + } + + @Override + public ByteBuf readChunk(ChannelHandlerContext ctx) { + return readChunk(ctx.alloc()); + } + + @Override + public ByteBuf readChunk(ByteBufAllocator allocator) { + ByteBuf buf = allocator.buffer(); + buf.writeInt(chunksProduced); + chunksProduced++; + return buf; + } + + @Override + public long length() { + return chunksToProduce; + } + + @Override + public long progress() { + return chunksProduced; + } + + boolean isClosed() { + return closed; + } + } + + private static final class ThrowingChunkedInput implements ChunkedInput { + private final Exception error; + + private volatile boolean closed; + + ThrowingChunkedInput(Exception error) { + this.error = error; + } + + @Override + public boolean isEndOfInput() { + return false; + } + + @Override + public void close() { + closed = true; + } + + @Override + public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { + return readChunk(ctx.alloc()); + } + + @Override + public ByteBuf readChunk(ByteBufAllocator allocator) throws Exception { + throw error; + } + + @Override + public long length() { + return -1; + } + + @Override + public long progress() { + return -1; + } + + boolean isClosed() { + return closed; + } + } } From 90ea3ec9f68d4c94e9fbbc94a8e9ad64e81e35ee Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 1 Mar 2019 06:47:56 +0100 Subject: [PATCH 392/417] Adjust tests to be able to build / test when using IBM J9 / OpenJ9 (#8900) Motivation: We should run a CI job using J9 to ensure netty also works when using different JVMs. Modifications: - Adjust PooledByteBufAllocatorTest to be able to complete faster when using a JVM which takes longer when joining Threads (this seems to be the case with J9). - Skip UDT tests on J9 as UDT is not supported there. Result: Be able to run CI against J9. --- .../buffer/PooledByteBufAllocatorTest.java | 19 ++++++++++++++----- .../util/internal/PlatformDependent.java | 14 ++++++++++++++ .../udt/UDTClientServerConnectionTest.java | 4 ++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java b/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java index 9116e9c73e22..39a613249387 100644 --- a/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java +++ b/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java @@ -430,8 +430,14 @@ public void testConcurrentUsage() throws Throwable { Thread.sleep(100); } } finally { + // First mark all AllocationThreads to complete their work and then wait until these are complete + // and rethrow if there was any error. for (AllocationThread t : threads) { - t.finish(); + t.markAsFinished(); + } + + for (AllocationThread t: threads) { + t.joinAndCheckForError(); } } } @@ -494,14 +500,17 @@ private void releaseBuffers() { } } - public boolean isFinished() { + boolean isFinished() { return finish.get() != null; } - public void finish() throws Throwable { + void markAsFinished() { + finish.compareAndSet(null, Boolean.TRUE); + } + + void joinAndCheckForError() throws Throwable { try { // Mark as finish if not already done but ensure we not override the previous set error. - finish.compareAndSet(null, Boolean.TRUE); join(); } finally { releaseBuffers(); @@ -509,7 +518,7 @@ public void finish() throws Throwable { checkForError(); } - public void checkForError() throws Throwable { + void checkForError() throws Throwable { Object obj = finish.get(); if (obj instanceof Throwable) { throw (Throwable) obj; diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index 1baeecbf7c90..0a70c36401c4 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -74,6 +74,7 @@ public final class PlatformDependent { private static final boolean IS_WINDOWS = isWindows0(); private static final boolean IS_OSX = isOsx0(); + private static final boolean IS_J9_JVM = isJ9Jvm0(); private static final boolean MAYBE_SUPER_USER; @@ -998,6 +999,19 @@ private static Throwable unsafeUnavailabilityCause0() { } } + /** + * Returns {@code true} if the running JVM is either IBM J9 or + * Eclipse OpenJ9, {@code false} otherwise. + */ + public static boolean isJ9Jvm() { + return IS_J9_JVM; + } + + private static boolean isJ9Jvm0() { + String vmName = SystemPropertyUtil.get("java.vm.name", "").toLowerCase(); + return vmName.startsWith("ibm j9") || vmName.startsWith("eclipse openj9"); + } + private static long maxDirectMemory0() { long maxDirectMemory = 0; diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java index c8786434aeb0..c6af2124e81f 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java @@ -35,6 +35,8 @@ import io.netty.util.NetUtil; import io.netty.util.concurrent.DefaultThreadFactory; import io.netty.util.concurrent.GlobalEventExecutor; +import io.netty.util.internal.PlatformDependent; +import org.junit.Assume; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -338,6 +340,8 @@ public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception */ @Test public void connection() throws Exception { + Assume.assumeFalse("Not supported on J9 JVM", PlatformDependent.isJ9Jvm()); + log.info("Starting server."); // Using LOCALHOST4 as UDT transport does not support IPV6 :( final Server server = new Server(new InetSocketAddress(NetUtil.LOCALHOST4, 0)); From ef3e98d905b675188d32e97e09c916a4b7edf8aa Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 1 Mar 2019 11:28:51 +0100 Subject: [PATCH 393/417] Add docker-compose config to run build with OpenJ9 JVM (#8903) Motivation: To ensure Netty works on different JVMs we should also run tests on the CI with these. Modifications: Add docker-compose config to run build with OpenJ9 JVM Result: Ensure Netty works with different JVMs --- docker/docker-compose.centos-6.openj9111.yaml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 docker/docker-compose.centos-6.openj9111.yaml diff --git a/docker/docker-compose.centos-6.openj9111.yaml b/docker/docker-compose.centos-6.openj9111.yaml new file mode 100644 index 000000000000..a7d08ddd51bb --- /dev/null +++ b/docker/docker-compose.centos-6.openj9111.yaml @@ -0,0 +1,22 @@ +version: "3" + +services: + + runtime-setup: + image: netty:centos-6-openj9-1.11 + build: + args: + centos_version : "6" + java_version : "adopt-openj9@1.11.0-2" + + test: + image: netty:centos-6-openj9-1.11 + + test-leak: + image: netty:centos-6-openj9-1.11 + + test-boringssl-static: + image: netty:centos-6-openj9-1.11 + + shell: + image: netty:centos-6-openj9-1.11 From 6f507dfeed8bb57f6107f7010af8b582eb246aa7 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 1 Mar 2019 19:31:06 +0100 Subject: [PATCH 394/417] Only remove ReferenceCountedOpenSslEngine from OpenSslEngineMap when engine is destroyed (#8905) Motivation: We must only remove ReferenceCountedOpenSslEngine from OpenSslEngineMap when engine is destroyed as the verifier / certificate callback may be called multiple times when the remote peer did initiate a renegotiation. If we fail to do so we will cause an NPE like this: ``` 13:16:36.750 [testsuite-oio-worker-5-18] DEBUG i.n.h.s.ReferenceCountedOpenSslServerContext - Failed to set the server-side key material java.lang.NullPointerException: null at io.netty.handler.ssl.OpenSslKeyMaterialManager.setKeyMaterialServerSide(OpenSslKeyMaterialManager.java:69) at io.netty.handler.ssl.ReferenceCountedOpenSslServerContext$OpenSslServerCertificateCallback.handle(ReferenceCountedOpenSslServerContext.java:212) at io.netty.internal.tcnative.SSL.readFromSSL(Native Method) at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.readPlaintextData(ReferenceCountedOpenSslEngine.java:575) at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.unwrap(ReferenceCountedOpenSslEngine.java:1124) at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.unwrap(ReferenceCountedOpenSslEngine.java:1236) at io.netty.handler.ssl.ReferenceCountedOpenSslEngine.unwrap(ReferenceCountedOpenSslEngine.java:1279) at io.netty.handler.ssl.SslHandler$SslEngineType$1.unwrap(SslHandler.java:217) at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1330) at io.netty.handler.ssl.SslHandler.decodeNonJdkCompatible(SslHandler.java:1237) at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1274) at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:502) at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:441) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:278) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:345) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:337) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1408) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:345) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930) at io.netty.channel.oio.AbstractOioByteChannel.doRead(AbstractOioByteChannel.java:170) at io.netty.channel.oio.AbstractOioChannel$1.run(AbstractOioChannel.java:40) at io.netty.channel.ThreadPerChannelEventLoop.run(ThreadPerChannelEventLoop.java:69) at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:905) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:834) ``` While the exception is kind of harmless (as we will reject the renegotiation at the end anyway) it produces some noise in the logs. Modifications: Don't remove engine from map after handshake is complete but wait for it to be removed until the engine is destroyed. Result: No more NPE and less noise in the logs. --- .../java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java | 1 - 1 file changed, 1 deletion(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index f93d283d43e0..b7df163bf2bf 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -1687,7 +1687,6 @@ private SSLEngineResult.HandshakeStatus handshake() throws SSLException { } // if SSL_do_handshake returns > 0 or sslError == SSL.SSL_ERROR_NAME it means the handshake was finished. session.handshakeFinished(); - engineMap.remove(ssl); return FINISHED; } From 14ef469f3170842fdf6e3f41c745017fcd9679e4 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 1 Mar 2019 19:42:29 +0100 Subject: [PATCH 395/417] Use maven plugin to prevent API/ABI breakage as part of build process (#8904) Motivation: Netty is very widely used which can lead to a lot of pain when we break API / ABI. We should make use japicmp-maven-plugin during the build to verify we do not introduce breakage by mistake. Modifications: - Add japicmp-maven-plugin to the build process - Fix a method signature change in HttpProxyHandler that was flagged as a possible problem. Result: Ensure no API/ABI breakage accour between releases. --- all/pom.xml | 1 + codec-http/pom.xml | 1 - example/pom.xml | 3 ++ .../netty/handler/proxy/HttpProxyHandler.java | 2 +- microbench/pom.xml | 2 +- pom.xml | 30 +++++++++++++++++++ testsuite-autobahn/pom.xml | 4 +++ testsuite-http2/pom.xml | 4 +++ testsuite-osgi/pom.xml | 1 + testsuite-shading/pom.xml | 1 + testsuite/pom.xml | 1 + 11 files changed, 47 insertions(+), 3 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index a9030dd9f5e0..ecdd9033af23 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -31,6 +31,7 @@ ${project.build.directory}/src ${project.build.directory}/versions + true diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 79d6b4ae34aa..bc644d6498b5 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -57,7 +57,6 @@ ${project.groupId} netty-handler ${project.version} - true com.jcraft diff --git a/example/pom.xml b/example/pom.xml index 89b2798e1241..5a84c30605ea 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -29,6 +29,9 @@ Netty/Example + + true + ${project.groupId} diff --git a/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java b/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java index c7c9519c4229..61088eb674f8 100644 --- a/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java +++ b/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java @@ -172,7 +172,7 @@ protected Object newInitialMessage(ChannelHandlerContext ctx) throws Exception { } @Override - protected boolean handleResponse(ChannelHandlerContext ctx, Object response) throws HttpProxyConnectException { + protected boolean handleResponse(ChannelHandlerContext ctx, Object response) throws Exception { if (response instanceof HttpResponse) { if (status != null) { throw new HttpProxyConnectException(exceptionMessage("too many responses"), /*headers=*/ null); diff --git a/microbench/pom.xml b/microbench/pom.xml index 0cfea6172273..4bf4a1a6fa87 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -38,8 +38,8 @@ + true - linux diff --git a/pom.xml b/pom.xml index 015e52f098a9..eae01bc37cdd 100644 --- a/pom.xml +++ b/pom.xml @@ -289,6 +289,7 @@ false false false + false @@ -670,6 +671,35 @@ + + com.github.siom79.japicmp + japicmp-maven-plugin + 0.13.1 + + + true + true + \d+\.\d+\.\d+\.Final + + + ^(?!io\.netty\.).* + ^io\.netty\.internal\.tcnative\..* + + + @io.netty.util.internal.UnstableApi + + + ${skipJapicmp} + + + + verify + + cmp + + + + maven-enforcer-plugin ${enforcer.plugin.version} diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index b8a4fddc8c4c..055196d4fd62 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -28,6 +28,10 @@ Netty/Testsuite/Autobahn + + true + + ${project.groupId} diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 409e2ed6aa01..254cbbf197cf 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -28,6 +28,10 @@ Netty/Testsuite/Http2 + + true + + ${project.groupId} diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index d484d29783cb..9a5345447281 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -31,6 +31,7 @@ 4.13.0 --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED + true diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index c97075b32e67..4967c08fdf4c 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -38,6 +38,7 @@ ${project.artifactId}-${project.version}.jar io.netty. + true diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 7d95d8f2f9b3..fe667b45185c 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -104,6 +104,7 @@ --add-exports java.base/sun.security.x509=ALL-UNNAMED + true From 452abd9b51adeeba2fdad8417f94f493e5bfee38 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 5 Mar 2019 09:10:26 +0100 Subject: [PATCH 396/417] Correctly monkey-patch id also in whe os / arch is used within library name. (#8913) Motivation: 2bb9f64e16dbb5cbbf691e284a97d745378a7b8a introduced a change which made it possible to use different shaded versions of netty-tcnative on the classpath. This only partly worked as we did not correctly handled the case when os / arch is part of the library name (which is the case when netty-tcnative-boringssl-static is used with the uber jar). Modifications: - If patching the ID failed we retry again with the os / arch stripped - Add unit tests to verify that patching ID now works with and without os / arch as suffix. Result: Using multiple shaded version of netty-tcnative-boringssl-static on MacOS works. --- .../util/internal/NativeLibraryLoader.java | 66 ++++++++++++----- .../internal/NativeLibraryLoaderTest.java | 74 +++++++++++++++++++ 2 files changed, 122 insertions(+), 18 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java b/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java index e74ca391619b..98e551c4eb66 100644 --- a/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java +++ b/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java @@ -184,28 +184,16 @@ public static void load(String originalName, ClassLoader loader) { in = url.openStream(); out = new FileOutputStream(tmpFile); - byte[] buffer = new byte[8192]; - int length; - if (TRY_TO_PATCH_SHADED_ID && PlatformDependent.isOsx() && !packagePrefix.isEmpty()) { - // We read the whole native lib into memory to make it easier to monkey-patch the id. - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(in.available()); - - while ((length = in.read(buffer)) > 0) { - byteArrayOutputStream.write(buffer, 0, length); - } - byteArrayOutputStream.flush(); - byte[] bytes = byteArrayOutputStream.toByteArray(); - byteArrayOutputStream.close(); - - // Try to patch the library id. - patchShadedLibraryId(bytes, originalName, name); - - out.write(bytes); + if (shouldShadedLibraryIdBePatched(packagePrefix)) { + patchShadedLibraryId(in, out, originalName, name); } else { + byte[] buffer = new byte[8192]; + int length; while ((length = in.read(buffer)) > 0) { out.write(buffer, 0, length); } } + out.flush(); // Close the output stream before loading the unpacked library, @@ -249,10 +237,50 @@ public static void load(String originalName, ClassLoader loader) { } } + // Package-private for testing. + static boolean patchShadedLibraryId(InputStream in, OutputStream out, String originalName, String name) + throws IOException { + byte[] buffer = new byte[8192]; + int length; + // We read the whole native lib into memory to make it easier to monkey-patch the id. + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(in.available()); + + while ((length = in.read(buffer)) > 0) { + byteArrayOutputStream.write(buffer, 0, length); + } + byteArrayOutputStream.flush(); + byte[] bytes = byteArrayOutputStream.toByteArray(); + byteArrayOutputStream.close(); + + final boolean patched; + // Try to patch the library id. + if (!patchShadedLibraryId(bytes, originalName, name)) { + // We did not find the Id, check if we used a originalName that has the os and arch as suffix. + // If this is the case we should also try to patch with the os and arch suffix removed. + String os = PlatformDependent.normalizedOs(); + String arch = PlatformDependent.normalizedArch(); + String osArch = "_" + os + "_" + arch; + if (originalName.endsWith(osArch)) { + patched = patchShadedLibraryId(bytes, + originalName.substring(0, originalName.length() - osArch.length()), name); + } else { + patched = false; + } + } else { + patched = true; + } + out.write(bytes, 0, bytes.length); + return patched; + } + + private static boolean shouldShadedLibraryIdBePatched(String packagePrefix) { + return TRY_TO_PATCH_SHADED_ID && PlatformDependent.isOsx() && !packagePrefix.isEmpty(); + } + /** * Try to patch shaded library to ensure it uses a unique ID. */ - private static void patchShadedLibraryId(byte[] bytes, String originalName, String name) { + private static boolean patchShadedLibraryId(byte[] bytes, String originalName, String name) { // Our native libs always have the name as part of their id so we can search for it and replace it // to make the ID unique if shading is used. byte[] nameBytes = originalName.getBytes(CharsetUtil.UTF_8); @@ -278,6 +306,7 @@ private static void patchShadedLibraryId(byte[] bytes, String originalName, Stri if (idIdx == -1) { logger.debug("Was not able to find the ID of the shaded native library {}, can't adjust it.", name); + return false; } else { // We found our ID... now monkey-patch it! for (int i = 0; i < nameBytes.length; i++) { @@ -291,6 +320,7 @@ private static void patchShadedLibraryId(byte[] bytes, String originalName, Stri "Found the ID of the shaded native library {}. Replacing ID part {} with {}", name, originalName, new String(bytes, idIdx, nameBytes.length, CharsetUtil.UTF_8)); } + return true; } } diff --git a/common/src/test/java/io/netty/util/internal/NativeLibraryLoaderTest.java b/common/src/test/java/io/netty/util/internal/NativeLibraryLoaderTest.java index de73b9ecf50f..e591b6cf3be1 100644 --- a/common/src/test/java/io/netty/util/internal/NativeLibraryLoaderTest.java +++ b/common/src/test/java/io/netty/util/internal/NativeLibraryLoaderTest.java @@ -15,11 +15,19 @@ */ package io.netty.util.internal; +import io.netty.util.CharsetUtil; import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; import java.util.UUID; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -65,4 +73,70 @@ private static void verifySuppressedException(UnsatisfiedLinkError error, throw new RuntimeException(e); } } + + @Test + public void testPatchingId() throws IOException { + testPatchingId0(true, false); + } + + @Test + public void testPatchingIdWithOsArch() throws IOException { + testPatchingId0(true, true); + } + + @Test + public void testPatchingIdNotMatch() throws IOException { + testPatchingId0(false, false); + } + + @Test + public void testPatchingIdWithOsArchNotMatch() throws IOException { + testPatchingId0(false, true); + } + + private static void testPatchingId0(boolean match, boolean withOsArch) throws IOException { + byte[] bytes = new byte[1024]; + PlatformDependent.threadLocalRandom().nextBytes(bytes); + byte[] idBytes = ("/workspace/netty-tcnative/boringssl-static/target/" + + "native-build/target/lib/libnetty_tcnative-2.0.20.Final.jnilib").getBytes(CharsetUtil.UTF_8); + + String originalName; + if (match) { + originalName = "netty-tcnative"; + } else { + originalName = "nonexist_tcnative"; + } + String name = "shaded_" + originalName; + if (withOsArch) { + name += "_osx_x86_64"; + } + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(bytes, 0, bytes.length); + out.write(idBytes, 0, idBytes.length); + out.write(bytes, 0 , bytes.length); + + out.flush(); + byte[] inBytes = out.toByteArray(); + out.close(); + + InputStream inputStream = new ByteArrayInputStream(inBytes); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try { + assertEquals(match, + NativeLibraryLoader.patchShadedLibraryId(inputStream, outputStream, originalName, name)); + + outputStream.flush(); + byte[] outputBytes = outputStream.toByteArray(); + assertArrayEquals(bytes, Arrays.copyOfRange(outputBytes, 0, bytes.length)); + byte[] patchedId = Arrays.copyOfRange(outputBytes, bytes.length, bytes.length + idBytes.length); + assertEquals(!match, Arrays.equals(idBytes, patchedId)); + assertArrayEquals(bytes, + Arrays.copyOfRange(outputBytes, bytes.length + idBytes.length, outputBytes.length)); + assertEquals(inBytes.length, outputBytes.length); + } finally { + inputStream.close(); + outputStream.close(); + } + } } From 39fcdb3e0db5686696784dd43f59d646a3beab73 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 5 Mar 2019 09:17:18 +0100 Subject: [PATCH 397/417] Support delegating task when using ReferenceCountedOpenSslEngine. (#8859) Motivation: SSLEngine API has a notion of tasks that may be expensive and offload these to another thread. We did not support this when using our native implementation but can now for various operations during the handshake. Modifications: - Support offloading tasks during the handshake when using our native SSLEngine implementation - Correctly handle the case when NEED_TASK is returned and nothing was consumed / produced yet Result: Be able to offload long running tasks from the EventLoop when using SslHandler with our native SSLEngine. --- .../ssl/ReferenceCountedOpenSslContext.java | 5 +- .../ssl/ReferenceCountedOpenSslEngine.java | 86 ++++++++++++++++--- .../java/io/netty/handler/ssl/SslHandler.java | 6 +- pom.xml | 2 +- 4 files changed, 82 insertions(+), 17 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java index f93544f3865a..6506e949a52b 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java @@ -86,7 +86,8 @@ public Integer run() { 2048)); } }); - + private static final boolean USE_TASKS = + SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.useTasks", false); private static final Integer DH_KEY_LENGTH; private static final ResourceLeakDetector leakDetector = ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ReferenceCountedOpenSslContext.class); @@ -342,6 +343,8 @@ public String run() { if (enableOcsp) { SSLContext.enableOcsp(ctx, isClient()); } + + SSLContext.setUseTasks(ctx, USE_TASKS); success = true; } finally { if (!success) { diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index b7df163bf2bf..c420a7ee7678 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -76,6 +76,7 @@ import static java.lang.Integer.MAX_VALUE; import static java.lang.Math.min; import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED; +import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_TASK; import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_UNWRAP; import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_WRAP; import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; @@ -169,6 +170,7 @@ private enum HandshakeState { private boolean receivedShutdown; private volatile int destroyed; private volatile String applicationProtocol; + private volatile boolean needTask; // Reference Counting private final ResourceLeakTracker leak; @@ -753,6 +755,10 @@ public final SSLEngineResult wrap( // we may have freed up space by flushing above. bytesProduced = bioLengthBefore - SSL.bioLengthByteBuffer(networkBIO); + if (status == NEED_TASK) { + return newResult(status, 0, bytesProduced); + } + if (bytesProduced > 0) { // If we have filled up the dst buffer and we have not finished the handshake we should try to // wrap again. Otherwise we should only try to wrap again if there is still data pending in @@ -883,6 +889,8 @@ public final SSLEngineResult wrap( // to write encrypted data to. This is an OVERFLOW condition. // [1] https://www.openssl.org/docs/manmaster/ssl/SSL_write.html return newResult(BUFFER_OVERFLOW, status, bytesConsumed, bytesProduced); + } else if (sslError == SSL.SSL_ERROR_WANT_X509_LOOKUP) { + return newResult(NEED_TASK, bytesConsumed, bytesProduced); } else { // Everything else is considered as error throw shutdownWithError("SSL_write", sslError); @@ -923,6 +931,10 @@ private SSLEngineResult newResult(SSLEngineResult.Status status, SSLEngineResult } return new SSLEngineResult(CLOSED, hs, bytesConsumed, bytesProduced); } + if (hs == NEED_TASK) { + // Set needTask to true so getHandshakeStatus() will return the correct value. + needTask = true; + } return new SSLEngineResult(status, hs, bytesConsumed, bytesProduced); } @@ -1020,6 +1032,11 @@ public final SSLEngineResult unwrap( } status = handshake(); + + if (status == NEED_TASK) { + return newResult(status, 0, 0); + } + if (status == NEED_WRAP) { return NEED_WRAP_OK; } @@ -1160,7 +1177,10 @@ public final SSLEngineResult unwrap( closeAll(); } return newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status, - bytesConsumed, bytesProduced); + bytesConsumed, bytesProduced); + } else if (sslError == SSL.SSL_ERROR_WANT_X509_LOOKUP) { + return newResult(isInboundDone() ? CLOSED : OK, + NEED_TASK, bytesConsumed, bytesProduced); } else { return sslReadErrorResult(sslError, SSL.getLastErrorNumber(), bytesConsumed, bytesProduced); @@ -1293,10 +1313,29 @@ public final synchronized SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] ds } @Override - public final Runnable getDelegatedTask() { - // Currently, we do not delegate SSL computation tasks - // TODO: in the future, possibly create tasks to do encrypt / decrypt async - return null; + public final synchronized Runnable getDelegatedTask() { + if (isDestroyed()) { + return null; + } + final Runnable task = SSL.getTask(ssl); + if (task == null) { + return null; + } + return new Runnable() { + @Override + public void run() { + try { + if (isDestroyed()) { + // The engine was destroyed in the meantime, just return. + return; + } + task.run(); + } finally { + // The task was run, reset needTask to false so getHandshakeStatus() returns the correct value. + needTask = false; + } + } + }; } @Override @@ -1610,7 +1649,10 @@ public final synchronized void beginHandshake() throws SSLException { throw RENEGOTIATION_UNSUPPORTED; case NOT_STARTED: handshakeState = HandshakeState.STARTED_EXPLICITLY; - handshake(); + if (handshake() == NEED_TASK) { + // Set needTask to true so getHandshakeStatus() will return the correct value. + needTask = true; + } calculateMaxWrapOverhead(); break; default: @@ -1680,10 +1722,18 @@ private SSLEngineResult.HandshakeStatus handshake() throws SSLException { int sslError = SSL.getError(ssl, code); if (sslError == SSL.SSL_ERROR_WANT_READ || sslError == SSL.SSL_ERROR_WANT_WRITE) { return pendingStatus(SSL.bioLengthNonApplication(networkBIO)); - } else { - // Everything else is considered as error - throw shutdownWithError("SSL_do_handshake", sslError); } + + if (sslError == SSL.SSL_ERROR_WANT_X509_LOOKUP) { + return NEED_TASK; + } + + // Everything else is considered as error + throw shutdownWithError("SSL_do_handshake", sslError); + } + // We have produced more data as part of the handshake if this is the case the user should call wrap(...) + if (SSL.bioLengthNonApplication(networkBIO) > 0) { + return NEED_WRAP; } // if SSL_do_handshake returns > 0 or sslError == SSL.SSL_ERROR_NAME it means the handshake was finished. session.handshakeFinished(); @@ -1703,12 +1753,26 @@ private SSLEngineResult.HandshakeStatus mayFinishHandshake(SSLEngineResult.Hands @Override public final synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() { // Check if we are in the initial handshake phase or shutdown phase - return needPendingStatus() ? pendingStatus(SSL.bioLengthNonApplication(networkBIO)) : NOT_HANDSHAKING; + if (needPendingStatus()) { + if (needTask) { + // There is a task outstanding + return NEED_TASK; + } + return pendingStatus(SSL.bioLengthNonApplication(networkBIO)); + } + return NOT_HANDSHAKING; } private SSLEngineResult.HandshakeStatus getHandshakeStatus(int pending) { // Check if we are in the initial handshake phase or shutdown phase - return needPendingStatus() ? pendingStatus(pending) : NOT_HANDSHAKING; + if (needPendingStatus()) { + if (needTask) { + // There is a task outstanding + return NEED_TASK; + } + return pendingStatus(pending); + } + return NOT_HANDSHAKING; } private boolean needPendingStatus() { diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index 6f327bcfc067..c3f74af4ef0b 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -1511,10 +1511,8 @@ private static void runAllDelegatedTasks(SSLEngine engine) { private boolean runDelegatedTasks(boolean inUnwrap) { if (delegatedTaskExecutor == ImmediateExecutor.INSTANCE || inEventLoop(delegatedTaskExecutor)) { // We should run the task directly in the EventExecutor thread and not offload at all. - for (;;) { - runAllDelegatedTasks(engine); - return true; - } + runAllDelegatedTasks(engine); + return true; } else { executeDelegatedTasks(inUnwrap); return false; diff --git a/pom.xml b/pom.xml index eae01bc37cdd..1d4cdf8ce2bc 100644 --- a/pom.xml +++ b/pom.xml @@ -274,7 +274,7 @@ fedora netty-tcnative - 2.0.20.Final + 2.0.22.Final ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber From a651804f9d9ca9b42ae5d4d9590228522f7851ba Mon Sep 17 00:00:00 2001 From: Oleksii Kachaiev Date: Wed, 6 Mar 2019 16:51:58 +0200 Subject: [PATCH 398/417] Carefully manage Keep-Alive connections in HttpStaticFileServer (#8914) Motivation: Simple rules: * close the connection when sending any error * specify "Connection: close" header when closing the connection * successful responses should keep the connection intact when otherwise is not requested by the client Modifications: * "send response and cleanup the connection" logic moved to a helper * for all successful responses set "Content-Lenght" header * do not specify "Connection: Keep-Alive" header as far it's a default for HTTP/1.1 * set "Connection: close" header when necessary Result: Keep-Alive connections management is inlined with RFCs. --- .../file/HttpStaticFileServerHandler.java | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java index 8095fe419cc1..d5e875102e49 100644 --- a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java +++ b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java @@ -122,6 +122,7 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr return; } + final boolean keepAlive = HttpUtil.isKeepAlive(request); final String uri = request.uri(); final String path = sanitizeUri(uri); if (path == null) { @@ -137,9 +138,9 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr if (file.isDirectory()) { if (uri.endsWith("/")) { - sendListing(ctx, file, uri); + sendListing(ctx, file, uri, keepAlive); } else { - sendRedirect(ctx, uri + '/'); + sendRedirect(ctx, uri + '/', keepAlive); } return; } @@ -160,7 +161,7 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000; long fileLastModifiedSeconds = file.lastModified() / 1000; if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) { - sendNotModified(ctx); + sendNotModified(ctx, keepAlive); return; } } @@ -178,8 +179,9 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr HttpUtil.setContentLength(response, fileLength); setContentTypeHeader(response, file); setDateAndCacheHeaders(response, file); - if (HttpUtil.isKeepAlive(request)) { - response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); + + if (!keepAlive) { + response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); } // Write the initial line and the header. @@ -218,7 +220,7 @@ public void operationComplete(ChannelProgressiveFuture future) { }); // Decide whether to close the connection or not. - if (!HttpUtil.isKeepAlive(request)) { + if (!keepAlive) { // Close the connection when the whole content is written out. lastContentFuture.addListener(ChannelFutureListener.CLOSE); } @@ -264,7 +266,7 @@ private static String sanitizeUri(String uri) { private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*"); - private static void sendListing(ChannelHandlerContext ctx, File dir, String dirPath) { + private static void sendListing(ChannelHandlerContext ctx, File dir, String dirPath, boolean keepAlive) { FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8"); @@ -304,16 +306,14 @@ private static void sendListing(ChannelHandlerContext ctx, File dir, String dirP response.content().writeBytes(buffer); buffer.release(); - // Close the connection as soon as the error message is sent. - ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + sendAndCleanupConnection(ctx, response, keepAlive); } - private static void sendRedirect(ChannelHandlerContext ctx, String newUri) { + private static void sendRedirect(ChannelHandlerContext ctx, String newUri, boolean keepAlive) { FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND); response.headers().set(HttpHeaderNames.LOCATION, newUri); - // Close the connection as soon as the error message is sent. - ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + sendAndCleanupConnection(ctx, response, keepAlive); } private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { @@ -321,8 +321,7 @@ private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus stat HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8)); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8"); - // Close the connection as soon as the error message is sent. - ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + sendAndCleanupConnection(ctx, response, false); } /** @@ -331,12 +330,32 @@ private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus stat * @param ctx * Context */ - private static void sendNotModified(ChannelHandlerContext ctx) { + private static void sendNotModified(ChannelHandlerContext ctx, boolean keepAlive) { FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED); setDateHeader(response); - // Close the connection as soon as the error message is sent. - ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + sendAndCleanupConnection(ctx, response, keepAlive); + } + + /** + * If Keep-Alive is disabled, attaches "Connection: close" header to the response + * and closes the connection after the response being sent. + */ + private static void sendAndCleanupConnection(ChannelHandlerContext ctx, FullHttpResponse response, + boolean keepAlive) { + HttpUtil.setContentLength(response, response.content().readableBytes()); + if (!keepAlive) { + // We're going to close the connection as soon as the response is sent, + // so we should also make it clear for the client. + response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); + } + + ChannelFuture flushPromise = ctx.writeAndFlush(response); + + if (!keepAlive) { + // Close the connection as soon as the response is sent. + flushPromise.addListener(ChannelFutureListener.CLOSE); + } } /** From 0de5402337c3c0dcf91b603e04a0dbaa75b05616 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 7 Mar 2019 09:36:59 +0100 Subject: [PATCH 399/417] Add interopt tests between Conscrypt and OpenSSL SSLEngine implementations. (#8919) Motivation: In the past we found a lot of SSL related bugs because of the interopt tests we have in place between different SSLEngine implementations. We should have as many of these interopt tests as possible for this reason. Modifications: - Add interopt tests between Conscrypt and OpenSSL SSLEngine implementations Result: More tests for SSL. --- .../ConscryptOpenSslEngineInteropTest.java | 143 ++++++++++++++++++ .../OpenSslConscryptSslEngineInteropTest.java | 143 ++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 handler/src/test/java/io/netty/handler/ssl/ConscryptOpenSslEngineInteropTest.java create mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslConscryptSslEngineInteropTest.java diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptOpenSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptOpenSslEngineInteropTest.java new file mode 100644 index 000000000000..9cb5bb610072 --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/ConscryptOpenSslEngineInteropTest.java @@ -0,0 +1,143 @@ +/* + * Copyright 2019 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import java.security.Provider; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory; +import static org.junit.Assume.assumeTrue; + +@RunWith(Parameterized.class) +public class ConscryptOpenSslEngineInteropTest extends ConscryptSslEngineTest { + + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}, delegate = {2}") + public static Collection data() { + List params = new ArrayList(); + for (BufferType type: BufferType.values()) { + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), false }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), true }); + } + return params; + } + + public ConscryptOpenSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo, boolean delegate) { + super(type, combo, delegate); + } + + @BeforeClass + public static void checkOpenssl() { + assumeTrue(OpenSsl.isAvailable()); + } + + @Override + protected SslProvider sslClientProvider() { + return SslProvider.JDK; + } + + @Override + protected SslProvider sslServerProvider() { + return SslProvider.OPENSSL; + } + + @Override + protected Provider serverSslContextProvider() { + return null; + } + + @Override + @Test + @Ignore("TODO: Make this work with Conscrypt") + public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth() { + super.testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(); + } + + @Override + @Test + @Ignore("TODO: Make this work with Conscrypt") + public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth() { + super.testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(); + } + + @Override + protected boolean mySetupMutualAuthServerIsValidClientException(Throwable cause) { + // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. + return super.mySetupMutualAuthServerIsValidClientException(cause) || causedBySSLException(cause); + } + + @Override + @Test + public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(); + } + + @Override + @Test + public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(); + } + + @Override + @Test + public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(); + } + + @Override + @Test + public void testClientHostnameValidationSuccess() throws InterruptedException, SSLException { + assumeTrue(OpenSsl.supportsHostnameValidation()); + super.testClientHostnameValidationSuccess(); + } + + @Override + @Test + public void testClientHostnameValidationFail() throws InterruptedException, SSLException { + assumeTrue(OpenSsl.supportsHostnameValidation()); + super.testClientHostnameValidationFail(); + } + + @Override + @Test + public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testSessionAfterHandshakeKeyManagerFactoryMutualAuth(); + } + + @Override + protected boolean mySetupMutualAuthServerIsValidServerException(Throwable cause) { + // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. + return super.mySetupMutualAuthServerIsValidServerException(cause) || causedBySSLException(cause); + } + + @Override + protected SSLEngine wrapEngine(SSLEngine engine) { + return Java8SslTestUtils.wrapSSLEngineForTesting(engine); + } +} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslConscryptSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslConscryptSslEngineInteropTest.java new file mode 100644 index 000000000000..fa49fa20cbdc --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslConscryptSslEngineInteropTest.java @@ -0,0 +1,143 @@ +/* + * Copyright 2019 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import java.security.Provider; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory; +import static org.junit.Assume.assumeTrue; + +@RunWith(Parameterized.class) +public class OpenSslConscryptSslEngineInteropTest extends ConscryptSslEngineTest { + + @Parameterized.Parameters(name = "{index}: bufferType = {0}, combo = {1}, delegate = {2}") + public static Collection data() { + List params = new ArrayList(); + for (BufferType type: BufferType.values()) { + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), false }); + params.add(new Object[] { type, ProtocolCipherCombo.tlsv12(), true }); + } + return params; + } + + public OpenSslConscryptSslEngineInteropTest(BufferType type, ProtocolCipherCombo combo, boolean delegate) { + super(type, combo, delegate); + } + + @BeforeClass + public static void checkOpenssl() { + assumeTrue(OpenSsl.isAvailable()); + } + + @Override + protected SslProvider sslClientProvider() { + return SslProvider.OPENSSL; + } + + @Override + protected SslProvider sslServerProvider() { + return SslProvider.JDK; + } + + @Override + protected Provider clientSslContextProvider() { + return null; + } + + @Override + @Test + @Ignore("TODO: Make this work with Conscrypt") + public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth() { + super.testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(); + } + + @Override + @Test + @Ignore("TODO: Make this work with Conscrypt") + public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth() { + super.testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(); + } + + @Override + protected boolean mySetupMutualAuthServerIsValidClientException(Throwable cause) { + // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. + return super.mySetupMutualAuthServerIsValidClientException(cause) || causedBySSLException(cause); + } + + @Override + @Test + public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(); + } + + @Override + @Test + public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(); + } + + @Override + @Test + public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(); + } + + @Override + @Test + public void testClientHostnameValidationSuccess() throws InterruptedException, SSLException { + assumeTrue(OpenSsl.supportsHostnameValidation()); + super.testClientHostnameValidationSuccess(); + } + + @Override + @Test + public void testClientHostnameValidationFail() throws InterruptedException, SSLException { + assumeTrue(OpenSsl.supportsHostnameValidation()); + super.testClientHostnameValidationFail(); + } + + @Override + @Test + public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testSessionAfterHandshakeKeyManagerFactoryMutualAuth(); + } + + @Override + protected boolean mySetupMutualAuthServerIsValidServerException(Throwable cause) { + // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. + return super.mySetupMutualAuthServerIsValidServerException(cause) || causedBySSLException(cause); + } + + @Override + protected SSLEngine wrapEngine(SSLEngine engine) { + return Java8SslTestUtils.wrapSSLEngineForTesting(engine); + } +} From 1725504a37dc8c1cfe69bd9384d43da9aca26117 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 7 Mar 2019 10:30:55 +0100 Subject: [PATCH 400/417] =?UTF-8?q?Do=20not=20use=20GetPrimitiveArrayCriti?= =?UTF-8?q?cal(...)=20due=20multiple=20not-fixed=20bugs=E2=80=A6=20(#8921)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Do not use GetPrimitiveArrayCritical(...) due multiple not-fixed bugs related to GCLocker Motivation: GetPrimitiveArrayCritical(...) may cause multiple not-fixed bugs related to the GCLocker while there is little gain for our use-case. We should just use GetByteArrayRegion(...) and copy into a small on-stack buffer. See also: - https://shipilev.net/jvm/anatomy-quarks/9-jni-critical-gclocker/#_g1 - https://bugs.openjdk.java.net/browse/JDK-8048556 - https://bugs.openjdk.java.net/browse/JDK-8057573 - https://bugs.openjdk.java.net/browse/JDK-8057586 Special thanks to @jayv @shipilev @apangin for the pointers. Modifications: Replace GetPrimitiveArrayCritical(...) with GetByteArrayRegion(...) Result: Less risks hitting GCLocker related bugs. --- .../src/main/c/netty_unix_socket.c | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/transport-native-unix-common/src/main/c/netty_unix_socket.c b/transport-native-unix-common/src/main/c/netty_unix_socket.c index 3dd0920b198f..b3133d672bab 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_socket.c +++ b/transport-native-unix-common/src/main/c/netty_unix_socket.c @@ -232,16 +232,25 @@ static jint _socket(JNIEnv* env, jclass clazz, int type) { int netty_unix_socket_initSockaddr(JNIEnv* env, jbyteArray address, jint scopeId, jint jport, const struct sockaddr_storage* addr, socklen_t* addrSize) { uint16_t port = htons((uint16_t) jport); + // We use 16 bytes as this allows us to fit ipv6, ipv4 and ipv4 mapped ipv6 addresses in the array. + jbyte addressBytes[16]; - // Use GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical to signal the VM that we really would like - // to not do a memory copy here. This is ok as we not do any blocking action here anyway. - // This is important as the VM may suspend GC for the time! - jbyte* addressBytes = (*env)->GetPrimitiveArrayCritical(env, address, 0); - if (addressBytes == NULL) { - // No memory left ?!?!? - netty_unix_errors_throwOutOfMemoryError(env); + int len = (*env)->GetArrayLength(env, address); + + if (len > 16) { + // This should never happen but let's guard against it anyway. return -1; } + + // We use GetByteArrayRegion(...) and copy into a small stack allocated buffer and NOT GetPrimitiveArrayCritical(...) + // as there are still multiple GCLocker related bugs which are not fixed yet. + // + // For example: + // https://bugs.openjdk.java.net/browse/JDK-8048556 + // https://bugs.openjdk.java.net/browse/JDK-8057573 + // https://bugs.openjdk.java.net/browse/JDK-8057586 + (*env)->GetByteArrayRegion(env, address, 0, len, addressBytes); + if (socketType == AF_INET6) { struct sockaddr_in6* ip6addr = (struct sockaddr_in6*) addr; *addrSize = sizeof(struct sockaddr_in6); @@ -262,8 +271,6 @@ int netty_unix_socket_initSockaddr(JNIEnv* env, jbyteArray address, jint scopeId ipaddr->sin_port = port; memcpy(&(ipaddr->sin_addr.s_addr), addressBytes + 12, 4); } - - (*env)->ReleasePrimitiveArrayCritical(env, address, addressBytes, JNI_ABORT); return 0; } From 67663fa7d171e224ce288a184cbe14563357ca34 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 7 Mar 2019 10:31:51 +0100 Subject: [PATCH 401/417] =?UTF-8?q?HttpContentDecoder=20must=20continue=20?= =?UTF-8?q?read=20when=20it=20did=20not=20produce=20any=20mes=E2=80=A6=20(?= =?UTF-8?q?#8922)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: When HttpContentDecoder (and so HttpContentDecompressor) does not produce any message we need to make sure it calls ctx.read() if auto read is false to not stale. Modifications: - Keep track if we need to call ctx.read() or not - Add unit test Result: Fixes https://github.com/netty/netty/issues/8915. --- .../codec/http/HttpContentDecoder.java | 173 ++++++++++-------- .../http/HttpContentDecompressorTest.java | 70 +++++++ 2 files changed, 166 insertions(+), 77 deletions(-) create mode 100644 codec-http/src/test/java/io/netty/handler/codec/http/HttpContentDecompressorTest.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java index 3eb7ad3e338e..eb7b7c0a6c23 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java @@ -51,102 +51,107 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder out) throws Exception { - if (msg instanceof HttpResponse && ((HttpResponse) msg).status().code() == 100) { + try { + if (msg instanceof HttpResponse && ((HttpResponse) msg).status().code() == 100) { - if (!(msg instanceof LastHttpContent)) { - continueResponse = true; + if (!(msg instanceof LastHttpContent)) { + continueResponse = true; + } + // 100-continue response must be passed through. + out.add(ReferenceCountUtil.retain(msg)); + return; } - // 100-continue response must be passed through. - out.add(ReferenceCountUtil.retain(msg)); - return; - } - if (continueResponse) { - if (msg instanceof LastHttpContent) { - continueResponse = false; + if (continueResponse) { + if (msg instanceof LastHttpContent) { + continueResponse = false; + } + // 100-continue response must be passed through. + out.add(ReferenceCountUtil.retain(msg)); + return; } - // 100-continue response must be passed through. - out.add(ReferenceCountUtil.retain(msg)); - return; - } - if (msg instanceof HttpMessage) { - cleanup(); - final HttpMessage message = (HttpMessage) msg; - final HttpHeaders headers = message.headers(); + if (msg instanceof HttpMessage) { + cleanup(); + final HttpMessage message = (HttpMessage) msg; + final HttpHeaders headers = message.headers(); - // Determine the content encoding. - String contentEncoding = headers.get(HttpHeaderNames.CONTENT_ENCODING); - if (contentEncoding != null) { - contentEncoding = contentEncoding.trim(); - } else { - contentEncoding = IDENTITY; - } - decoder = newContentDecoder(contentEncoding); + // Determine the content encoding. + String contentEncoding = headers.get(HttpHeaderNames.CONTENT_ENCODING); + if (contentEncoding != null) { + contentEncoding = contentEncoding.trim(); + } else { + contentEncoding = IDENTITY; + } + decoder = newContentDecoder(contentEncoding); - if (decoder == null) { - if (message instanceof HttpContent) { - ((HttpContent) message).retain(); + if (decoder == null) { + if (message instanceof HttpContent) { + ((HttpContent) message).retain(); + } + out.add(message); + return; } - out.add(message); - return; - } - // Remove content-length header: - // the correct value can be set only after all chunks are processed/decoded. - // If buffering is not an issue, add HttpObjectAggregator down the chain, it will set the header. - // Otherwise, rely on LastHttpContent message. - if (headers.contains(HttpHeaderNames.CONTENT_LENGTH)) { - headers.remove(HttpHeaderNames.CONTENT_LENGTH); - headers.set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - } - // Either it is already chunked or EOF terminated. - // See https://github.com/netty/netty/issues/5892 + // Remove content-length header: + // the correct value can be set only after all chunks are processed/decoded. + // If buffering is not an issue, add HttpObjectAggregator down the chain, it will set the header. + // Otherwise, rely on LastHttpContent message. + if (headers.contains(HttpHeaderNames.CONTENT_LENGTH)) { + headers.remove(HttpHeaderNames.CONTENT_LENGTH); + headers.set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); + } + // Either it is already chunked or EOF terminated. + // See https://github.com/netty/netty/issues/5892 - // set new content encoding, - CharSequence targetContentEncoding = getTargetContentEncoding(contentEncoding); - if (HttpHeaderValues.IDENTITY.contentEquals(targetContentEncoding)) { - // Do NOT set the 'Content-Encoding' header if the target encoding is 'identity' - // as per: http://tools.ietf.org/html/rfc2616#section-14.11 - headers.remove(HttpHeaderNames.CONTENT_ENCODING); - } else { - headers.set(HttpHeaderNames.CONTENT_ENCODING, targetContentEncoding); - } + // set new content encoding, + CharSequence targetContentEncoding = getTargetContentEncoding(contentEncoding); + if (HttpHeaderValues.IDENTITY.contentEquals(targetContentEncoding)) { + // Do NOT set the 'Content-Encoding' header if the target encoding is 'identity' + // as per: http://tools.ietf.org/html/rfc2616#section-14.11 + headers.remove(HttpHeaderNames.CONTENT_ENCODING); + } else { + headers.set(HttpHeaderNames.CONTENT_ENCODING, targetContentEncoding); + } - if (message instanceof HttpContent) { - // If message is a full request or response object (headers + data), don't copy data part into out. - // Output headers only; data part will be decoded below. - // Note: "copy" object must not be an instance of LastHttpContent class, - // as this would (erroneously) indicate the end of the HttpMessage to other handlers. - HttpMessage copy; - if (message instanceof HttpRequest) { - HttpRequest r = (HttpRequest) message; // HttpRequest or FullHttpRequest - copy = new DefaultHttpRequest(r.protocolVersion(), r.method(), r.uri()); - } else if (message instanceof HttpResponse) { - HttpResponse r = (HttpResponse) message; // HttpResponse or FullHttpResponse - copy = new DefaultHttpResponse(r.protocolVersion(), r.status()); + if (message instanceof HttpContent) { + // If message is a full request or response object (headers + data), don't copy data part into out. + // Output headers only; data part will be decoded below. + // Note: "copy" object must not be an instance of LastHttpContent class, + // as this would (erroneously) indicate the end of the HttpMessage to other handlers. + HttpMessage copy; + if (message instanceof HttpRequest) { + HttpRequest r = (HttpRequest) message; // HttpRequest or FullHttpRequest + copy = new DefaultHttpRequest(r.protocolVersion(), r.method(), r.uri()); + } else if (message instanceof HttpResponse) { + HttpResponse r = (HttpResponse) message; // HttpResponse or FullHttpResponse + copy = new DefaultHttpResponse(r.protocolVersion(), r.status()); + } else { + throw new CodecException("Object of class " + message.getClass().getName() + + " is not a HttpRequest or HttpResponse"); + } + copy.headers().set(message.headers()); + copy.setDecoderResult(message.decoderResult()); + out.add(copy); } else { - throw new CodecException("Object of class " + message.getClass().getName() + - " is not a HttpRequest or HttpResponse"); + out.add(message); } - copy.headers().set(message.headers()); - copy.setDecoderResult(message.decoderResult()); - out.add(copy); - } else { - out.add(message); } - } - if (msg instanceof HttpContent) { - final HttpContent c = (HttpContent) msg; - if (decoder == null) { - out.add(c.retain()); - } else { - decodeContent(c, out); + if (msg instanceof HttpContent) { + final HttpContent c = (HttpContent) msg; + if (decoder == null) { + out.add(c.retain()); + } else { + decodeContent(c, out); + } } + } finally { + needRead = out.isEmpty(); } } @@ -170,6 +175,20 @@ private void decodeContent(HttpContent c, List out) { } } + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + boolean needRead = this.needRead; + this.needRead = true; + + try { + ctx.fireChannelReadComplete(); + } finally { + if (needRead && !ctx.channel().config().isAutoRead()) { + ctx.read(); + } + } + } + /** * Returns a new {@link EmbeddedChannel} that decodes the HTTP message * content encoded in the specified contentEncoding. diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentDecompressorTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentDecompressorTest.java new file mode 100644 index 000000000000..4a659fad5ed6 --- /dev/null +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentDecompressorTest.java @@ -0,0 +1,70 @@ +/* + * Copyright 2019 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: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.http; + +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicInteger; + +public class HttpContentDecompressorTest { + + // See https://github.com/netty/netty/issues/8915. + @Test + public void testInvokeReadWhenNotProduceMessage() { + final AtomicInteger readCalled = new AtomicInteger(); + EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter() { + @Override + public void read(ChannelHandlerContext ctx) { + readCalled.incrementAndGet(); + ctx.read(); + } + }, new HttpContentDecompressor(), new ChannelInboundHandlerAdapter() { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + ctx.fireChannelRead(msg); + ctx.read(); + } + }); + + channel.config().setAutoRead(false); + + readCalled.set(0); + HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); + response.headers().set(HttpHeaderNames.CONTENT_ENCODING, "gzip"); + response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json;charset=UTF-8"); + response.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); + + Assert.assertTrue(channel.writeInbound(response)); + + // we triggered read explicitly + Assert.assertEquals(1, readCalled.get()); + + Assert.assertTrue(channel.readInbound() instanceof HttpResponse); + + Assert.assertFalse(channel.writeInbound(new DefaultHttpContent(Unpooled.EMPTY_BUFFER))); + + // read was triggered by the HttpContentDecompressor itself as it did not produce any message to the next + // inbound handler. + Assert.assertEquals(2, readCalled.get()); + Assert.assertFalse(channel.finishAndReleaseAll()); + } +} From 3e24e9f6ff7f763874a8e048fdf38ea55e61550e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 8 Mar 2019 06:47:28 +0100 Subject: [PATCH 402/417] =?UTF-8?q?ReferenceCountedOpenSslEngines=20SSLSes?= =?UTF-8?q?sion=20must=20provide=20local=20certific=E2=80=A6=20(#8918)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: The SSLSession that is returned by SSLEngine.getHandshakeSession() must be able to provide the local certificates when the TrustManager is invoked on the server-side. Modifications: - Correctly return the local certificates - Add unit test Result: Be able to obtain local certificates from handshake SSLSession during verification on the server side. --- .../ssl/ReferenceCountedOpenSslEngine.java | 5 +- .../ssl/ConscryptJdkSslEngineInteropTest.java | 7 + .../handler/ssl/ConscryptSslEngineTest.java | 7 + .../ssl/JdkConscryptSslEngineInteropTest.java | 7 + .../ssl/JdkOpenSslEngineInteroptTest.java | 6 + .../netty/handler/ssl/OpenSslEngineTest.java | 6 + .../ssl/OpenSslJdkSslEngineInteroptTest.java | 6 + .../io/netty/handler/ssl/SSLEngineTest.java | 150 ++++++++++++++++++ 8 files changed, 191 insertions(+), 3 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java index c420a7ee7678..4627d9502bc1 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java @@ -2001,7 +2001,6 @@ private final class DefaultOpenSslSession implements OpenSslSession { // thread. private X509Certificate[] x509PeerCerts; private Certificate[] peerCerts; - private Certificate[] localCerts; private String protocol; private String cipher; @@ -2156,7 +2155,6 @@ public void handshakeFinished() throws SSLException { id = SSL.getSessionId(ssl); cipher = toJavaCipherSuite(SSL.getCipherForSSL(ssl)); protocol = SSL.getVersion(ssl); - localCerts = localCertificateChain; initPeerCerts(); selectApplicationProtocol(); @@ -2291,6 +2289,7 @@ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { @Override public Certificate[] getLocalCertificates() { + Certificate[] localCerts = ReferenceCountedOpenSslEngine.this.localCertificateChain; if (localCerts == null) { return null; } @@ -2317,7 +2316,7 @@ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { @Override public Principal getLocalPrincipal() { - Certificate[] local = localCerts; + Certificate[] local = ReferenceCountedOpenSslEngine.this.localCertificateChain; if (local == null || local.length == 0) { return null; } diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java index 6a31dac906e7..da2d76757fd5 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java @@ -87,4 +87,11 @@ public void testSessionBindingEvent() throws Exception { // Ignore due bug in Conscrypt where the incorrect SSLSession object is used in the SSLSessionBindingEvent. // See https://github.com/google/conscrypt/issues/593 } + + @Ignore("Ignore due bug in Conscrypt") + @Override + public void testHandshakeSession() throws Exception { + // Ignore as Conscrypt does not correctly return the local certificates while the TrustManager is invoked. + // See https://github.com/google/conscrypt/issues/634 + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java index 15a68bdf61ef..114552f37347 100644 --- a/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java @@ -85,4 +85,11 @@ public void testSessionBindingEvent() throws Exception { // Ignore due bug in Conscrypt where the incorrect SSLSession object is used in the SSLSessionBindingEvent. // See https://github.com/google/conscrypt/issues/593 } + + @Ignore("Ignore due bug in Conscrypt") + @Override + public void testHandshakeSession() throws Exception { + // Ignore as Conscrypt does not correctly return the local certificates while the TrustManager is invoked. + // See https://github.com/google/conscrypt/issues/634 + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java index 771713a95593..d7aa08fab400 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java @@ -85,4 +85,11 @@ protected boolean mySetupMutualAuthServerIsValidClientException(Throwable cause) // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. return super.mySetupMutualAuthServerIsValidClientException(cause) || causedBySSLException(cause); } + + @Ignore("Ignore due bug in Conscrypt") + @Override + public void testHandshakeSession() throws Exception { + // Ignore as Conscrypt does not correctly return the local certificates while the TrustManager is invoked. + // See https://github.com/google/conscrypt/issues/634 + } } diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java index 677730c47579..23e004bb842e 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java @@ -128,6 +128,12 @@ protected boolean mySetupMutualAuthServerIsValidClientException(Throwable cause) return super.mySetupMutualAuthServerIsValidClientException(cause) || causedBySSLException(cause); } + @Override + public void testHandshakeSession() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testHandshakeSession(); + } + @Override protected SSLEngine wrapEngine(SSLEngine engine) { return Java8SslTestUtils.wrapSSLEngineForTesting(engine); diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java index 3ee9f4bfe495..c90a69fd7f27 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java @@ -160,6 +160,12 @@ public void testClientHostnameValidationFail() throws InterruptedException, SSLE super.testClientHostnameValidationFail(); } + @Override + public void testHandshakeSession() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testHandshakeSession(); + } + private static boolean isNpnSupported(String versionString) { String[] versionStringParts = versionString.split(" ", -1); if (versionStringParts.length == 2 && "LibreSSL".equals(versionStringParts[0])) { diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java index 0bd9af843fb6..440c34c25b33 100644 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java @@ -127,6 +127,12 @@ protected boolean mySetupMutualAuthServerIsValidServerException(Throwable cause) return super.mySetupMutualAuthServerIsValidServerException(cause) || causedBySSLException(cause); } + @Override + public void testHandshakeSession() throws Exception { + checkShouldUseKeyManagerFactory(); + super.testHandshakeSession(); + } + @Override protected SSLEngine wrapEngine(SSLEngine engine) { return Java8SslTestUtils.wrapSSLEngineForTesting(engine); diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 51b02aa18579..9ecb615daabf 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -36,6 +36,7 @@ import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.SelfSignedCertificate; +import io.netty.handler.ssl.util.SimpleTrustManagerFactory; import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; import io.netty.util.ReferenceCountUtil; @@ -56,13 +57,18 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; +import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; +import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.Provider; +import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.ArrayList; @@ -94,6 +100,7 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactorySpi; +import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; import javax.security.cert.X509Certificate; @@ -3049,6 +3056,149 @@ private void testSessionAfterHandshake0(boolean useKeyManagerFactory, boolean mu } } + @Test + public void testHandshakeSession() throws Exception { + final SelfSignedCertificate ssc = new SelfSignedCertificate(); + + final TestTrustManagerFactory clientTmf = new TestTrustManagerFactory(ssc.cert()); + final TestTrustManagerFactory serverTmf = new TestTrustManagerFactory(ssc.cert()); + + clientSslCtx = SslContextBuilder.forClient() + .trustManager(new SimpleTrustManagerFactory() { + @Override + protected void engineInit(KeyStore keyStore) { + // NOOP + } + + @Override + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { + // NOOP + } + + @Override + protected TrustManager[] engineGetTrustManagers() { + return new TrustManager[] { clientTmf }; + } + }) + .keyManager(newKeyManagerFactory(ssc)) + .sslProvider(sslClientProvider()) + .sslContextProvider(clientSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .build(); + serverSslCtx = SslContextBuilder.forServer(newKeyManagerFactory(ssc)) + .trustManager(new SimpleTrustManagerFactory() { + @Override + protected void engineInit(KeyStore keyStore) { + // NOOP + } + + @Override + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { + // NOOP + } + + @Override + protected TrustManager[] engineGetTrustManagers() { + return new TrustManager[] { serverTmf }; + } + }) + .sslProvider(sslServerProvider()) + .sslContextProvider(serverSslContextProvider()) + .protocols(protocols()) + .ciphers(ciphers()) + .clientAuth(ClientAuth.REQUIRE) + .build(); + SSLEngine clientEngine = null; + SSLEngine serverEngine = null; + try { + clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); + handshake(clientEngine, serverEngine); + + assertTrue(clientTmf.isVerified()); + assertTrue(serverTmf.isVerified()); + } finally { + cleanupClientSslEngine(clientEngine); + cleanupServerSslEngine(serverEngine); + ssc.delete(); + } + } + + private KeyManagerFactory newKeyManagerFactory(SelfSignedCertificate ssc) + throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, + CertificateException, IOException { + return SslContext.buildKeyManagerFactory( + new java.security.cert.X509Certificate[] { ssc.cert() }, ssc.key(), null, null); + } + + private final class TestTrustManagerFactory extends X509ExtendedTrustManager { + private final Certificate localCert; + private volatile boolean verified; + + TestTrustManagerFactory(Certificate localCert) { + this.localCert = localCert; + } + + boolean isVerified() { + return verified; + } + + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] x509Certificates, String s, Socket socket) { + fail(); + } + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] x509Certificates, String s, Socket socket) { + fail(); + } + + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) { + verified = true; + assertFalse(sslEngine.getUseClientMode()); + SSLSession session = sslEngine.getHandshakeSession(); + assertNotNull(session); + Certificate[] localCertificates = session.getLocalCertificates(); + assertNotNull(localCertificates); + assertEquals(1, localCertificates.length); + assertEquals(localCert, localCertificates[0]); + assertNotNull(session.getLocalPrincipal()); + } + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) { + verified = true; + assertTrue(sslEngine.getUseClientMode()); + SSLSession session = sslEngine.getHandshakeSession(); + assertNotNull(session); + assertNull(session.getLocalCertificates()); + assertNull(session.getLocalPrincipal()); + } + + @Override + public void checkClientTrusted( + java.security.cert.X509Certificate[] x509Certificates, String s) { + fail(); + } + + @Override + public void checkServerTrusted( + java.security.cert.X509Certificate[] x509Certificates, String s) { + fail(); + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return EmptyArrays.EMPTY_X509_CERTIFICATES; + } + } + protected SSLEngine wrapEngine(SSLEngine engine) { return engine; } From b2eaab092b68f3c61f1e14f39e38c964e5095dd7 Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Thu, 7 Mar 2019 21:55:11 -0800 Subject: [PATCH 403/417] Optimize Hpack and AsciiString hashcode and equals (#8902) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: While looking at hpack header-processing hotspots I noticed some low level too-big-to-inline methods which can be shrunk. Modifications: Reduce bytecode size and/or runtime operations used for the following methods: PlatformDependent0.equals(byte[], ...) PlatformDependent0.equalsConstantTime(byte[], ...) PlatformDependent0.hashCodeAscii(byte[],int,int) PlatformDependent.hashCodeAscii(CharSequence) Result: Existing benchmarks show decent improvement Before Benchmark (size) Mode Cnt Score Error Units HpackUtilBenchmark.newEquals SMALL thrpt 5 17200229.374 ± 1701239.198 ops/s HpackUtilBenchmark.newEquals MEDIUM thrpt 5 3386061.629 ± 72264.685 ops/s HpackUtilBenchmark.newEquals LARGE thrpt 5 507579.209 ± 65883.951 ops/s After Benchmark (size) Mode Cnt Score Error Units HpackUtilBenchmark.newEquals SMALL thrpt 5 29221527.058 ± 4805825.836 ops/s HpackUtilBenchmark.newEquals MEDIUM thrpt 5 6556251.645 ± 466115.199 ops/s HpackUtilBenchmark.newEquals LARGE thrpt 5 879828.889 ± 148136.641 ops/s Before Benchmark (size) Mode Cnt Score Error Units PlatformDepBench.unsafeBytesEqual 4 avgt 10 4.263 ± 0.110 ns/op PlatformDepBench.unsafeBytesEqual 10 avgt 10 5.206 ± 0.133 ns/op PlatformDepBench.unsafeBytesEqual 50 avgt 10 8.160 ± 0.320 ns/op PlatformDepBench.unsafeBytesEqual 100 avgt 10 13.810 ± 0.751 ns/op PlatformDepBench.unsafeBytesEqual 1000 avgt 10 89.077 ± 7.275 ns/op PlatformDepBench.unsafeBytesEqual 10000 avgt 10 773.940 ± 24.579 ns/op PlatformDepBench.unsafeBytesEqual 100000 avgt 10 7546.807 ± 110.395 ns/op After Benchmark (size) Mode Cnt Score Error Units PlatformDepBench.unsafeBytesEqual 4 avgt 10 3.337 ± 0.087 ns/op PlatformDepBench.unsafeBytesEqual 10 avgt 10 4.286 ± 0.194 ns/op PlatformDepBench.unsafeBytesEqual 50 avgt 10 7.817 ± 0.123 ns/op PlatformDepBench.unsafeBytesEqual 100 avgt 10 11.260 ± 0.412 ns/op PlatformDepBench.unsafeBytesEqual 1000 avgt 10 84.255 ± 2.596 ns/op PlatformDepBench.unsafeBytesEqual 10000 avgt 10 591.892 ± 5.136 ns/op PlatformDepBench.unsafeBytesEqual 100000 avgt 10 6978.859 ± 285.043 ns/op --- .../util/internal/PlatformDependent.java | 100 +++++---------- .../util/internal/PlatformDependent0.java | 120 ++++++++---------- 2 files changed, 80 insertions(+), 140 deletions(-) diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index 0a70c36401c4..8696996fcbfb 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -754,83 +754,43 @@ public static int hashCodeAscii(byte[] bytes, int startPos, int length) { * The resulting hash code will be case insensitive. */ public static int hashCodeAscii(CharSequence bytes) { + final int length = bytes.length(); + final int remainingBytes = length & 7; int hash = HASH_CODE_ASCII_SEED; - final int remainingBytes = bytes.length() & 7; // Benchmarking shows that by just naively looping for inputs 8~31 bytes long we incur a relatively large // performance penalty (only achieve about 60% performance of loop which iterates over each char). So because // of this we take special provisions to unroll the looping for these conditions. - switch (bytes.length()) { - case 31: - case 30: - case 29: - case 28: - case 27: - case 26: - case 25: - case 24: - hash = hashCodeAsciiCompute(bytes, bytes.length() - 24, - hashCodeAsciiCompute(bytes, bytes.length() - 16, - hashCodeAsciiCompute(bytes, bytes.length() - 8, hash))); - break; - case 23: - case 22: - case 21: - case 20: - case 19: - case 18: - case 17: - case 16: - hash = hashCodeAsciiCompute(bytes, bytes.length() - 16, - hashCodeAsciiCompute(bytes, bytes.length() - 8, hash)); - break; - case 15: - case 14: - case 13: - case 12: - case 11: - case 10: - case 9: - case 8: - hash = hashCodeAsciiCompute(bytes, bytes.length() - 8, hash); - break; - case 7: - case 6: - case 5: - case 4: - case 3: - case 2: - case 1: - case 0: - break; - default: - for (int i = bytes.length() - 8; i >= remainingBytes; i -= 8) { - hash = hashCodeAsciiCompute(bytes, i, hash); + if (length >= 32) { + for (int i = length - 8; i >= remainingBytes; i -= 8) { + hash = hashCodeAsciiCompute(bytes, i, hash); + } + } else if (length >= 8) { + hash = hashCodeAsciiCompute(bytes, length - 8, hash); + if (length >= 16) { + hash = hashCodeAsciiCompute(bytes, length - 16, hash); + if (length >= 24) { + hash = hashCodeAsciiCompute(bytes, length - 24, hash); } - break; + } } - switch(remainingBytes) { - case 7: - return ((hash * HASH_CODE_C1 + hashCodeAsciiSanitizeByte(bytes.charAt(0))) - * HASH_CODE_C2 + hashCodeAsciiSanitizeShort(bytes, 1)) - * HASH_CODE_C1 + hashCodeAsciiSanitizeInt(bytes, 3); - case 6: - return (hash * HASH_CODE_C1 + hashCodeAsciiSanitizeShort(bytes, 0)) - * HASH_CODE_C2 + hashCodeAsciiSanitizeInt(bytes, 2); - case 5: - return (hash * HASH_CODE_C1 + hashCodeAsciiSanitizeByte(bytes.charAt(0))) - * HASH_CODE_C2 + hashCodeAsciiSanitizeInt(bytes, 1); - case 4: - return hash * HASH_CODE_C1 + hashCodeAsciiSanitizeInt(bytes, 0); - case 3: - return (hash * HASH_CODE_C1 + hashCodeAsciiSanitizeByte(bytes.charAt(0))) - * HASH_CODE_C2 + hashCodeAsciiSanitizeShort(bytes, 1); - case 2: - return hash * HASH_CODE_C1 + hashCodeAsciiSanitizeShort(bytes, 0); - case 1: - return hash * HASH_CODE_C1 + hashCodeAsciiSanitizeByte(bytes.charAt(0)); - default: - return hash; + if (remainingBytes == 0) { + return hash; + } + int offset = 0; + if (remainingBytes != 2 & remainingBytes != 4 & remainingBytes != 6) { // 1, 3, 5, 7 + hash = hash * HASH_CODE_C1 + hashCodeAsciiSanitizeByte(bytes.charAt(0)); + offset = 1; + } + if (remainingBytes != 1 & remainingBytes != 4 & remainingBytes != 5) { // 2, 3, 6, 7 + hash = hash * (offset == 0 ? HASH_CODE_C1 : HASH_CODE_C2) + + hashCodeAsciiSanitize(hashCodeAsciiSanitizeShort(bytes, offset)); + offset += 2; + } + if (remainingBytes >= 4) { // 4, 5, 6, 7 + return hash * ((offset == 0 | offset == 3) ? HASH_CODE_C1 : HASH_CODE_C2) + + hashCodeAsciiSanitizeInt(bytes, offset); } + return hash; } private static final class Mpsc { diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java index df45d1614a0e..edf965893cfa 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java @@ -622,72 +622,57 @@ static void setMemory(Object o, long offset, long bytes, byte value) { } static boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) { - if (length <= 0) { - return true; - } - final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1; - final long baseOffset2 = BYTE_ARRAY_BASE_OFFSET + startPos2; int remainingBytes = length & 7; - final long end = baseOffset1 + remainingBytes; - for (long i = baseOffset1 - 8 + length, j = baseOffset2 - 8 + length; i >= end; i -= 8, j -= 8) { - if (UNSAFE.getLong(bytes1, i) != UNSAFE.getLong(bytes2, j)) { - return false; + final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1; + final long diff = startPos2 - startPos1; + if (length >= 8) { + final long end = baseOffset1 + remainingBytes; + for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) { + if (UNSAFE.getLong(bytes1, i) != UNSAFE.getLong(bytes2, i + diff)) { + return false; + } } } - if (remainingBytes >= 4) { remainingBytes -= 4; - if (UNSAFE.getInt(bytes1, baseOffset1 + remainingBytes) != - UNSAFE.getInt(bytes2, baseOffset2 + remainingBytes)) { + long pos = baseOffset1 + remainingBytes; + if (UNSAFE.getInt(bytes1, pos) != UNSAFE.getInt(bytes2, pos + diff)) { return false; } } + final long baseOffset2 = baseOffset1 + diff; if (remainingBytes >= 2) { return UNSAFE.getChar(bytes1, baseOffset1) == UNSAFE.getChar(bytes2, baseOffset2) && - (remainingBytes == 2 || bytes1[startPos1 + 2] == bytes2[startPos2 + 2]); + (remainingBytes == 2 || + UNSAFE.getByte(bytes1, baseOffset1 + 2) == UNSAFE.getByte(bytes2, baseOffset2 + 2)); } - return bytes1[startPos1] == bytes2[startPos2]; + return remainingBytes == 0 || + UNSAFE.getByte(bytes1, baseOffset1) == UNSAFE.getByte(bytes2, baseOffset2); } static int equalsConstantTime(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) { long result = 0; + long remainingBytes = length & 7; final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1; - final long baseOffset2 = BYTE_ARRAY_BASE_OFFSET + startPos2; - final int remainingBytes = length & 7; final long end = baseOffset1 + remainingBytes; - for (long i = baseOffset1 - 8 + length, j = baseOffset2 - 8 + length; i >= end; i -= 8, j -= 8) { - result |= UNSAFE.getLong(bytes1, i) ^ UNSAFE.getLong(bytes2, j); - } - switch (remainingBytes) { - case 7: - return ConstantTimeUtils.equalsConstantTime(result | - (UNSAFE.getInt(bytes1, baseOffset1 + 3) ^ UNSAFE.getInt(bytes2, baseOffset2 + 3)) | - (UNSAFE.getChar(bytes1, baseOffset1 + 1) ^ UNSAFE.getChar(bytes2, baseOffset2 + 1)) | - (UNSAFE.getByte(bytes1, baseOffset1) ^ UNSAFE.getByte(bytes2, baseOffset2)), 0); - case 6: - return ConstantTimeUtils.equalsConstantTime(result | - (UNSAFE.getInt(bytes1, baseOffset1 + 2) ^ UNSAFE.getInt(bytes2, baseOffset2 + 2)) | - (UNSAFE.getChar(bytes1, baseOffset1) ^ UNSAFE.getChar(bytes2, baseOffset2)), 0); - case 5: - return ConstantTimeUtils.equalsConstantTime(result | - (UNSAFE.getInt(bytes1, baseOffset1 + 1) ^ UNSAFE.getInt(bytes2, baseOffset2 + 1)) | - (UNSAFE.getByte(bytes1, baseOffset1) ^ UNSAFE.getByte(bytes2, baseOffset2)), 0); - case 4: - return ConstantTimeUtils.equalsConstantTime(result | - (UNSAFE.getInt(bytes1, baseOffset1) ^ UNSAFE.getInt(bytes2, baseOffset2)), 0); - case 3: - return ConstantTimeUtils.equalsConstantTime(result | - (UNSAFE.getChar(bytes1, baseOffset1 + 1) ^ UNSAFE.getChar(bytes2, baseOffset2 + 1)) | - (UNSAFE.getByte(bytes1, baseOffset1) ^ UNSAFE.getByte(bytes2, baseOffset2)), 0); - case 2: - return ConstantTimeUtils.equalsConstantTime(result | - (UNSAFE.getChar(bytes1, baseOffset1) ^ UNSAFE.getChar(bytes2, baseOffset2)), 0); - case 1: - return ConstantTimeUtils.equalsConstantTime(result | - (UNSAFE.getByte(bytes1, baseOffset1) ^ UNSAFE.getByte(bytes2, baseOffset2)), 0); - default: - return ConstantTimeUtils.equalsConstantTime(result, 0); + final long diff = startPos2 - startPos1; + for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) { + result |= UNSAFE.getLong(bytes1, i) ^ UNSAFE.getLong(bytes2, i + diff); } + if (remainingBytes >= 4) { + result |= UNSAFE.getInt(bytes1, baseOffset1) ^ UNSAFE.getInt(bytes2, baseOffset1 + diff); + remainingBytes -= 4; + } + if (remainingBytes >= 2) { + long pos = end - remainingBytes; + result |= UNSAFE.getChar(bytes1, pos) ^ UNSAFE.getChar(bytes2, pos + diff); + remainingBytes -= 2; + } + if (remainingBytes == 1) { + long pos = end - 1; + result |= UNSAFE.getByte(bytes1, pos) ^ UNSAFE.getByte(bytes2, pos + diff); + } + return ConstantTimeUtils.equalsConstantTime(result, 0); } static boolean isZero(byte[] bytes, int startPos, int length) { @@ -718,35 +703,30 @@ static boolean isZero(byte[] bytes, int startPos, int length) { static int hashCodeAscii(byte[] bytes, int startPos, int length) { int hash = HASH_CODE_ASCII_SEED; - final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos; + long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos; final int remainingBytes = length & 7; final long end = baseOffset + remainingBytes; for (long i = baseOffset - 8 + length; i >= end; i -= 8) { hash = hashCodeAsciiCompute(UNSAFE.getLong(bytes, i), hash); } - switch(remainingBytes) { - case 7: - return ((hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset))) - * HASH_CODE_C2 + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset + 1))) - * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset + 3)); - case 6: - return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset))) - * HASH_CODE_C2 + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset + 2)); - case 5: - return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset))) - * HASH_CODE_C2 + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset + 1)); - case 4: - return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset)); - case 3: - return (hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset))) - * HASH_CODE_C2 + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset + 1)); - case 2: - return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset)); - case 1: - return hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset)); - default: + if (remainingBytes == 0) { return hash; } + int hcConst = HASH_CODE_C1; + if (remainingBytes != 2 & remainingBytes != 4 & remainingBytes != 6) { // 1, 3, 5, 7 + hash = hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset)); + hcConst = HASH_CODE_C2; + baseOffset++; + } + if (remainingBytes != 1 & remainingBytes != 4 & remainingBytes != 5) { // 2, 3, 6, 7 + hash = hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset)); + hcConst = hcConst == HASH_CODE_C1 ? HASH_CODE_C2 : HASH_CODE_C1; + baseOffset += 2; + } + if (remainingBytes >= 4) { // 4, 5, 6, 7 + return hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset)); + } + return hash; } static int hashCodeAsciiCompute(long value, int hash) { From ff7a9fa091a8bf2e10020f83fc4df1c44098bbbb Mon Sep 17 00:00:00 2001 From: root Date: Fri, 8 Mar 2019 08:51:34 +0000 Subject: [PATCH 404/417] [maven-release-plugin] prepare release netty-4.1.34.Final --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 6 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 76 insertions(+), 72 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index ecdd9033af23..c66f8dde807e 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 3e3c3eb2e2a2..e0d5fc517bbe 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.34.Final-SNAPSHOT + 4.1.34.Final pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.34.Final @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec-dns - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec-haproxy - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec-http - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec-http2 - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec-memcache - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec-mqtt - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec-redis - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec-smtp - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec-socks - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec-stomp - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-codec-xml - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-common - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-dev-tools - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-handler - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-handler-proxy - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-resolver - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-resolver-dns - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-transport - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-transport-rxtx - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-transport-sctp - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-transport-udt - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-example - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-all - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-transport-native-unix-common - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-transport-native-unix-common - 4.1.34.Final-SNAPSHOT + 4.1.34.Final linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.34.Final-SNAPSHOT + 4.1.34.Final osx-x86_64 io.netty netty-transport-native-epoll - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-transport-native-epoll - 4.1.34.Final-SNAPSHOT + 4.1.34.Final linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.34.Final-SNAPSHOT + 4.1.34.Final io.netty netty-transport-native-kqueue - 4.1.34.Final-SNAPSHOT + 4.1.34.Final osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 54227975158b..f9f2ad2768ce 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 8cee4876ef92..f43568ba68f2 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 2a339a631747..48c75ae43a58 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index bc644d6498b5..278158df82e2 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 5dfd308433c6..68378e555e63 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index c8f4a83a2be2..1d7b47433e84 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index d56c5ce645b9..343b18becc43 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 2f652f6d080f..05696fbc9b01 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index 98f3e2825005..9cc53e3a35cc 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 02fdf797faa2..453571a9c16b 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index ad2c54088748..141c7f1d90b5 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index e54653b437c0..d7fc49bb3bd6 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 51a28a5452f1..5000cc2ce759 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-codec diff --git a/common/pom.xml b/common/pom.xml index 95821508b857..ceb6626aab78 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 4a61abebf5e8..54db082d859e 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.34.Final-SNAPSHOT + 4.1.34.Final Netty/Dev-Tools @@ -50,4 +50,8 @@ + + + netty-4.1.34.Final + diff --git a/example/pom.xml b/example/pom.xml index 5a84c30605ea..7c2539eae224 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index b3e553a4c6bc..aa6edf748762 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index 134e41cfd49f..a68cfb22e688 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 4bf4a1a6fa87..1eddd3c3511b 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-microbench diff --git a/pom.xml b/pom.xml index 1d4cdf8ce2bc..6b23abef53af 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.34.Final-SNAPSHOT + 4.1.34.Final Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - HEAD + netty-4.1.34.Final diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index 3faa65a80171..98c1b1f8b20e 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index 8bda0b96947e..abc62e106d91 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 210290bf3dab..59c7c0cb46e6 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 055196d4fd62..f2bc1812ee38 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 254cbbf197cf..431ecb1f3ea6 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 9a5345447281..918bbe3d7f66 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index 4967c08fdf4c..9958ea993e8c 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index fe667b45185c..e60f64137d04 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index e6eb48bbd7e0..c037482604bd 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 41a4db66c77b..0dd93b1ea740 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 2345715e01ac..386e5d284559 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index cf7d2ed05771..4aba1ace7a55 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index 0188e8673c62..b383ea70bbc0 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 1f098ed03fa5..ed88e46e9ef8 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 2b54e728014e..8beb66bc3579 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index afab6a66e767..db657591bddc 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final-SNAPSHOT + 4.1.34.Final netty-transport From dec312810eb910bb99230249f0a5a352504ed217 Mon Sep 17 00:00:00 2001 From: Jaroslaw Date: Mon, 30 Sep 2019 16:59:48 +0200 Subject: [PATCH 405/417] fix errors after netty 4.1.34 merge --- buffer/src/test/java/io/netty/buffer/PoolArenaTest.java | 2 +- .../netty/handler/codec/http2/TestChannelInitializer.java | 5 +++++ .../main/java/io/netty/buffer/ByteBufAccessBenchmark.java | 2 +- pom.xml | 7 ++++++- transport-native-epoll/pom.xml | 2 +- .../main/java/io/netty/channel/epoll/EpollEventLoop.java | 4 +--- .../src/test/java/io/netty/channel/epoll/LibAIOTest.java | 1 + .../src/main/java/io/netty/channel/unix/Buffer.java | 2 +- 8 files changed, 17 insertions(+), 8 deletions(-) diff --git a/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java b/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java index 2c1bd12db32b..0e18eaf3f049 100644 --- a/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java +++ b/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java @@ -59,7 +59,7 @@ public void testDirectArenaOffsetCacheLine() throws Exception { long address = PlatformDependent.directBufferAddress(bb); Assert.assertEquals(0, (offset + address) & (alignment - 1)); - PlatformDependent.freeDirectBuffer(bb); + PlatformDependent.freeDirectWithCleaner(bb); } } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java index 2cc79d14e579..e8bbab678ef0 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java @@ -116,6 +116,11 @@ public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) { public void readComplete() { // Nothing needs to be done or adjusted after each read cycle is completed. } + + @Override + public void channelClosed() { + // noop + } }; } } diff --git a/microbench/src/main/java/io/netty/buffer/ByteBufAccessBenchmark.java b/microbench/src/main/java/io/netty/buffer/ByteBufAccessBenchmark.java index c7cad7ba0744..2614408a9fb3 100644 --- a/microbench/src/main/java/io/netty/buffer/ByteBufAccessBenchmark.java +++ b/microbench/src/main/java/io/netty/buffer/ByteBufAccessBenchmark.java @@ -66,7 +66,7 @@ public ByteBuf touch() { } @Override public boolean release() { - PlatformDependent.freeDirectBuffer(byteBuffer); + PlatformDependent.freeDirectWithCleaner(byteBuffer); return true; } } diff --git a/pom.xml b/pom.xml index 6b23abef53af..8b0cd38f9b1a 100644 --- a/pom.xml +++ b/pom.xml @@ -289,7 +289,7 @@ false false false - false + true @@ -789,6 +789,11 @@ java.net.StandardSocketOptions java.nio.channels.NetworkChannel + + java.nio.channels.AsynchronousFileChannel + java.nio.channels.CompletionHandler + java.util.concurrent.CompletableFuture + sun.security.x509.AlgorithmId sun.security.x509.CertificateAlgorithmId diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index c037482604bd..03d1becd216a 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -34,7 +34,7 @@ ${project.build.directory}/unix-common-lib ${unix.common.lib.dir}/META-INF/native/lib ${unix.common.lib.dir}/META-INF/native/include - LDFLAGS=-L${unix.common.lib.unpacked.dir} -Wl,--no-as-needed -lrt -Wl,--whole-archive -l${unix.common.lib.name} -Wl,--no-whole-archive + LDFLAGS=-L${unix.common.lib.unpacked.dir} -Wl,--no-as-needed -lrt -laio -Wl,--whole-archive -l${unix.common.lib.name} -Wl,--no-whole-archive true diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index 22b46c8318b7..6ac2ce53667b 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -32,8 +32,6 @@ import io.netty.util.internal.logging.InternalLoggerFactory; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; import java.util.Iterator; import java.util.Queue; import java.util.concurrent.Executor; @@ -46,7 +44,7 @@ /** * {@link EventLoop} which uses epoll under the covers. Only works on Linux! */ -final class EpollEventLoop extends SingleThreadEventLoop { +public class EpollEventLoop extends SingleThreadEventLoop { private static final InternalLogger logger = InternalLoggerFactory.getInstance(EpollEventLoop.class); protected static final AtomicIntegerFieldUpdater WAKEN_UP_UPDATER = AtomicIntegerFieldUpdater.newUpdater(EpollEventLoop.class, "wakenUp"); diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/LibAIOTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/LibAIOTest.java index 9c1575709f19..da1bb7957834 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/LibAIOTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/LibAIOTest.java @@ -531,6 +531,7 @@ static ByteBuffer allocateAlignedByteBuffer(int capacity, long align) { } } + @SuppressForbidden(reason = "libaio") static void freeAlignedByteBuffer(ByteBuffer buffer) { PlatformDependent.freeDirectNoCleaner((ByteBuffer) ((DirectBuffer) buffer).attachment()); } diff --git a/transport-native-unix-common/src/main/java/io/netty/channel/unix/Buffer.java b/transport-native-unix-common/src/main/java/io/netty/channel/unix/Buffer.java index ae23bf891d0f..7404e8ad4ba8 100644 --- a/transport-native-unix-common/src/main/java/io/netty/channel/unix/Buffer.java +++ b/transport-native-unix-common/src/main/java/io/netty/channel/unix/Buffer.java @@ -30,7 +30,7 @@ private Buffer() { } * Free the direct {@link ByteBuffer}. */ public static void free(ByteBuffer buffer) { - PlatformDependent.freeDirectBuffer(buffer); + PlatformDependent.freeDirectWithCleaner(buffer); } /** From 09e434cb781305de1daab65c163ee8e1e90b3b13 Mon Sep 17 00:00:00 2001 From: Jaroslaw Date: Mon, 30 Sep 2019 14:36:42 +0200 Subject: [PATCH 406/417] Version bump to 4.1.34.1.dse --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 4 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 73 insertions(+), 73 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 2ceaa5ae1961..7e943cc9db26 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-all diff --git a/bom/pom.xml b/bom/pom.xml index e0d5fc517bbe..3755afe04244 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.34.Final + 4.1.34.1.dse pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.34.Final + netty-4.1.34.1.dse @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec-dns - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec-haproxy - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec-http - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec-http2 - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec-memcache - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec-mqtt - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec-redis - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec-smtp - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec-socks - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec-stomp - 4.1.34.Final + 4.1.34.1.dse io.netty netty-codec-xml - 4.1.34.Final + 4.1.34.1.dse io.netty netty-common - 4.1.34.Final + 4.1.34.1.dse io.netty netty-dev-tools - 4.1.34.Final + 4.1.34.1.dse io.netty netty-handler - 4.1.34.Final + 4.1.34.1.dse io.netty netty-handler-proxy - 4.1.34.Final + 4.1.34.1.dse io.netty netty-resolver - 4.1.34.Final + 4.1.34.1.dse io.netty netty-resolver-dns - 4.1.34.Final + 4.1.34.1.dse io.netty netty-transport - 4.1.34.Final + 4.1.34.1.dse io.netty netty-transport-rxtx - 4.1.34.Final + 4.1.34.1.dse io.netty netty-transport-sctp - 4.1.34.Final + 4.1.34.1.dse io.netty netty-transport-udt - 4.1.34.Final + 4.1.34.1.dse io.netty netty-example - 4.1.34.Final + 4.1.34.1.dse io.netty netty-all - 4.1.34.Final + 4.1.34.1.dse io.netty netty-transport-native-unix-common - 4.1.34.Final + 4.1.34.1.dse io.netty netty-transport-native-unix-common - 4.1.34.Final + 4.1.34.1.dse linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.34.Final + 4.1.34.1.dse osx-x86_64 io.netty netty-transport-native-epoll - 4.1.34.Final + 4.1.34.1.dse io.netty netty-transport-native-epoll - 4.1.34.Final + 4.1.34.1.dse linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.34.Final + 4.1.34.1.dse io.netty netty-transport-native-kqueue - 4.1.34.Final + 4.1.34.1.dse osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index f9f2ad2768ce..5ab107f734e4 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index f43568ba68f2..60e58834602f 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 48c75ae43a58..1372db45e4a6 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 278158df82e2..60d9ab8a9d8b 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 68378e555e63..3cecd4af64ab 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 1d7b47433e84..286aed58df82 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 343b18becc43..85326434b30c 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 05696fbc9b01..cad555df74df 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index 9cc53e3a35cc..0b7db8cde8a4 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 453571a9c16b..0fab8a1600c9 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 141c7f1d90b5..a6064c0bf2d2 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index d7fc49bb3bd6..9dc14e327340 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 5000cc2ce759..f90e27842430 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-codec diff --git a/common/pom.xml b/common/pom.xml index ceb6626aab78..467b83d49334 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 54db082d859e..7984bb5eff3d 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.34.Final + 4.1.34.1.dse Netty/Dev-Tools @@ -52,6 +52,6 @@ - netty-4.1.34.Final + netty-4.1.34.1.dse diff --git a/example/pom.xml b/example/pom.xml index 7c2539eae224..6b118acedc4e 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index aa6edf748762..e99350ab3221 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index a68cfb22e688..51afe7ed3a70 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 1eddd3c3511b..1e81064f04fd 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-microbench diff --git a/pom.xml b/pom.xml index 8b0cd38f9b1a..790ffab1eb13 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.34.Final + 4.1.34.1.dse Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.34.Final + netty-4.1.34.1.dse diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index 98c1b1f8b20e..1ced2d2672ab 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index abc62e106d91..efece1580219 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 59c7c0cb46e6..09af27930dab 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index f2bc1812ee38..34601d3edea1 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 431ecb1f3ea6..7cd651a6cfab 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 918bbe3d7f66..67435598c177 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index 9958ea993e8c..2c71bd40dd37 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index e60f64137d04..a88c56f68546 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 03d1becd216a..2e3bbbcc9314 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index 0dd93b1ea740..db9a10077d3b 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 386e5d284559..0ea1cea25196 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 4aba1ace7a55..63d4b2f4adf6 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index b383ea70bbc0..3a6b9adeb480 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index ed88e46e9ef8..d3e7c5a65347 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 8beb66bc3579..f5e81f43e7c1 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index db657591bddc..6b9da8cf07b3 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.Final + 4.1.34.1.dse netty-transport From 44a09b5c355e5bc0928ee607908f15eac559b842 Mon Sep 17 00:00:00 2001 From: Jaroslaw Date: Tue, 1 Oct 2019 15:50:17 +0200 Subject: [PATCH 407/417] publishing fixes (repo has changed) --- build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.yaml b/build.yaml index f07a35b5c801..3b4bc8375bbc 100644 --- a/build.yaml +++ b/build.yaml @@ -11,12 +11,12 @@ build: echo "OS VERSION ===== $OS_VERSION" if [ "$OS_VERSION" = "osx/high-sierra" ]; then mvn -B clean -DskipTests - mvn -B -pl transport-native-unix-common,transport-native-kqueue -Partifactory deploy -DskipTests -DaltDeploymentRepository="artifactory::default::https://repo.datastax.com/datastax-releases-local" + mvn -B -pl transport-native-unix-common,transport-native-kqueue -Partifactory deploy -DskipTests -DaltDeploymentRepository="artifactory::default::https://repo.sjc.dsinternal.org/artifactory/datastax-releases-local" else export DEBIAN_FRONTEND=noninteractive export MAVEN_HOME=/home/jenkins/.mvn/apache-maven-3.2.5 export PATH=$MAVEN_HOME/bin:$PATH sudo apt-get update sudo apt-get install -y autoconf automake libtool make tar gcc-multilib libaio-dev - mvn -B clean deploy -Partifactory -DskipTests -DaltDeploymentRepository="artifactory::default::https://repo.datastax.com/datastax-releases-local" + mvn -B clean deploy -Partifactory -DskipTests -DaltDeploymentRepository="artifactory::default::https://repo.sjc.dsinternal.org/artifactory/datastax-releases-local" fi From bd8a1e6c4f083099420ad86a2d573914f810f1d7 Mon Sep 17 00:00:00 2001 From: Jaroslaw Date: Mon, 7 Oct 2019 16:57:32 +0200 Subject: [PATCH 408/417] workaround for issue with publishing OSX artifacts Jenkins job that publishes netty artifacts is splitted in two subjobs. First publishes OSX artifacts, seconds publishes all other artifacts. The former depends on the latter but this dependency is not reflected in jenkins. During a first run OSX subjob fails due to unresolved dependencies (artifacts published by the second subjob). Add -U to workaround caching "unresolved dependencies". --- build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.yaml b/build.yaml index 3b4bc8375bbc..5211ea4a0464 100644 --- a/build.yaml +++ b/build.yaml @@ -11,7 +11,7 @@ build: echo "OS VERSION ===== $OS_VERSION" if [ "$OS_VERSION" = "osx/high-sierra" ]; then mvn -B clean -DskipTests - mvn -B -pl transport-native-unix-common,transport-native-kqueue -Partifactory deploy -DskipTests -DaltDeploymentRepository="artifactory::default::https://repo.sjc.dsinternal.org/artifactory/datastax-releases-local" + mvn -U -B -pl transport-native-unix-common,transport-native-kqueue -Partifactory deploy -DskipTests -DaltDeploymentRepository="artifactory::default::https://repo.sjc.dsinternal.org/artifactory/datastax-releases-local" else export DEBIAN_FRONTEND=noninteractive export MAVEN_HOME=/home/jenkins/.mvn/apache-maven-3.2.5 From 11fbd259e110af2873ce7fc62856d48860e20123 Mon Sep 17 00:00:00 2001 From: Jake Luciani Date: Thu, 3 Oct 2019 14:01:11 -0400 Subject: [PATCH 409/417] Build from centos 6 to support old glibc DSP-18603 Docker setup --- build.yaml | 13 +++--------- dev-tools/pom.xml | 4 ++-- docker-datastax-release.sh | 15 ++++++++++++++ docker/Dockerfile-netty-centos6 | 36 +++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 12 deletions(-) create mode 100755 docker-datastax-release.sh create mode 100644 docker/Dockerfile-netty-centos6 diff --git a/build.yaml b/build.yaml index 5211ea4a0464..81ab0c48282a 100644 --- a/build.yaml +++ b/build.yaml @@ -3,20 +3,13 @@ schedules: schedule: adhoc os: - osx/high-sierra - - ubuntu/trusty64 -java: - - oraclejdk8 + - ubuntu/xenial64 build: - script: | echo "OS VERSION ===== $OS_VERSION" if [ "$OS_VERSION" = "osx/high-sierra" ]; then mvn -B clean -DskipTests - mvn -U -B -pl transport-native-unix-common,transport-native-kqueue -Partifactory deploy -DskipTests -DaltDeploymentRepository="artifactory::default::https://repo.sjc.dsinternal.org/artifactory/datastax-releases-local" + mvn -B -U -pl transport-native-unix-common,transport-native-kqueue -Partifactory deploy -DskipTests -DaltDeploymentRepository="artifactory::default::https://repo.sjc.dsinternal.org/artifactory/datastax-releases-local" else - export DEBIAN_FRONTEND=noninteractive - export MAVEN_HOME=/home/jenkins/.mvn/apache-maven-3.2.5 - export PATH=$MAVEN_HOME/bin:$PATH - sudo apt-get update - sudo apt-get install -y autoconf automake libtool make tar gcc-multilib libaio-dev - mvn -B clean deploy -Partifactory -DskipTests -DaltDeploymentRepository="artifactory::default::https://repo.sjc.dsinternal.org/artifactory/datastax-releases-local" + ./docker-datastax-release.sh fi diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 7984bb5eff3d..c839f7b2fa56 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -16,12 +16,12 @@ --> 4.0.0 - + io.netty netty-dev-tools diff --git a/docker-datastax-release.sh b/docker-datastax-release.sh new file mode 100755 index 000000000000..40812056919e --- /dev/null +++ b/docker-datastax-release.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +if [ ! -f /usr/bin/docker ]; then + export DEBIAN_FRONTEND=noninteractive + sudo apt-get update + sudo apt-get install -y software-properties-common + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + sudo add-apt-repository \ + "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" + sudo apt-get update + sudo apt-get install -y docker-ce +fi + +sudo docker build -f docker/Dockerfile-netty-centos6 -t netty-centos6 . +sudo docker run -t -v ~/.m2:/root/.m2 -v ~/.ssh:/root/.ssh -v ~/.gnupg:/root/.gnupg -v `pwd`:/code -w /code --entrypoint="" netty-centos6 bash -ic "mvn -B clean deploy -Partifactory -DskipTests -DaltDeploymentRepository=\"artifactory::default::https://repo.sjc.dsinternal.org/artifactory/datastax-releases-local\"" diff --git a/docker/Dockerfile-netty-centos6 b/docker/Dockerfile-netty-centos6 new file mode 100644 index 000000000000..de868debd493 --- /dev/null +++ b/docker/Dockerfile-netty-centos6 @@ -0,0 +1,36 @@ +FROM centos:6 +MAINTAINER netty@googlegroups.com +ENTRYPOINT /bin/bash + +ENV SOURCE_DIR $HOME/source +ENV MAVEN_VERSION 3.2.5 +ENV JAVA_VERSION 1.8.0 + +RUN mkdir $SOURCE_DIR +WORKDIR $SOURCE_DIR + +# install dependencies +RUN yum install -y \ + apr-devel \ + autoconf \ + automake \ + git \ + glibc-devel \ + java-$JAVA_VERSION-openjdk-devel \ + libtool \ + lksctp-tools \ + lsb-core \ + make \ + openssl-devel \ + tar \ + wget \ + libaio \ + libaio-devel + +RUN wget -q http://archive.apache.org/dist/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz && tar xfz apache-maven-$MAVEN_VERSION-bin.tar.gz && mv apache-maven-$MAVEN_VERSION /opt/ + +RUN echo 'PATH=/opt/apache-maven-$MAVEN_VERSION/bin:$PATH' >> ~/.bashrc + +RUN echo 'export JAVA_HOME="/usr/lib/jvm/java-$JAVA_VERSION/"' >> ~/.bashrc + +RUN rm -rf $SOURCE_DIR From fcaf37fa0bb7cd475cd1b6224f2b3855d73c54d9 Mon Sep 17 00:00:00 2001 From: Nick Hill Date: Fri, 22 Mar 2019 03:18:10 -0700 Subject: [PATCH 410/417] Fix possible ByteBuf leak when CompositeByteBuf is resized (#8946) Motivation: The special case fixed in #8497 also requires that we keep a derived slice when trimming components in place, as done by the capacity(int) and discardReadBytes() methods. Modifications: Ensure that we keep a ref to trimmed components' original retained slice in capacity(int) and discardReadBytes() methods, so that it is released properly when the they are later freed. Add unit test which fails prior to the fix. Result: Edge case leak is eliminated. --- .../io/netty/buffer/CompositeByteBuf.java | 15 ++++++-- .../buffer/AbstractCompositeByteBufTest.java | 34 +++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index dbd69a163b31..99127cbd2e06 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -753,7 +753,12 @@ public CompositeByteBuf capacity(int newCapacity) { if (bytesToTrim < cLength) { // Trim the last component c.endOffset -= bytesToTrim; - c.slice = null; + ByteBuf slice = c.slice; + if (slice != null) { + // We must replace the cached slice with a derived one to ensure that + // it can later be released properly in the case of PooledSlicedByteBuf. + c.slice = slice.slice(0, c.length()); + } break; } c.free(); @@ -1732,10 +1737,16 @@ public CompositeByteBuf discardReadBytes() { } firstComponentId++; } else { + int trimmedBytes = readerIndex - c.offset; c.offset = 0; c.endOffset -= readerIndex; c.adjustment += readerIndex; - c.slice = null; + ByteBuf slice = c.slice; + if (slice != null) { + // We must replace the cached slice with a derived one to ensure that + // it can later be released properly in the case of PooledSlicedByteBuf. + c.slice = slice.slice(trimmedBytes, c.length()); + } } removeCompRange(0, firstComponentId); diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java index e6bebe43cec6..ffeebaac9c46 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java @@ -1252,6 +1252,40 @@ public void testReleasesOnShrink() { assertEquals(0, b2.refCnt()); } + @Test + public void testReleasesOnShrink2() { + // It is important to use a pooled allocator here to ensure + // the slices returned by readRetainedSlice are of type + // PooledSlicedByteBuf, which maintains an independent refcount + // (so that we can be sure to cover this case) + ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer(); + + buffer.writeShort(1).writeShort(2); + + ByteBuf b1 = buffer.readRetainedSlice(2); + ByteBuf b2 = b1.retainedSlice(b1.readerIndex(), 2); + + // composite takes ownership of b1 and b2 + ByteBuf composite = Unpooled.compositeBuffer() + .addComponents(b1, b2); + + assertEquals(4, composite.capacity()); + + // reduce capacity down to two, will drop the second component + composite.capacity(2); + assertEquals(2, composite.capacity()); + + // releasing composite should release the components + composite.release(); + assertEquals(0, composite.refCnt()); + assertEquals(0, b1.refCnt()); + assertEquals(0, b2.refCnt()); + + // release last remaining ref to buffer + buffer.release(); + assertEquals(0, buffer.refCnt()); + } + @Test public void testAllocatorIsSameWhenCopy() { testAllocatorIsSameWhenCopy(false); From 136266b53bfaae60ab0769d87ed36b0317020477 Mon Sep 17 00:00:00 2001 From: Jaroslaw Date: Tue, 22 Oct 2019 08:45:28 +0200 Subject: [PATCH 411/417] DSP-19407 bump netty version to temp 4.1.34.2 --- all/pom.xml | 2 +- bom/pom.xml | 68 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 4 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 4 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 73 insertions(+), 73 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 7e943cc9db26..53319253ff0c 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 3755afe04244..7a9fee8b3d3b 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.34.1.dse + 4.1.34.2.dse pom Netty/BOM @@ -49,7 +49,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.34.1.dse + netty-4.1.34.2.dse @@ -69,165 +69,165 @@ io.netty netty-buffer - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec-dns - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec-haproxy - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec-http - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec-http2 - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec-memcache - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec-mqtt - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec-redis - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec-smtp - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec-socks - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec-stomp - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-codec-xml - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-common - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-dev-tools - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-handler - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-handler-proxy - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-resolver - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-resolver-dns - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-transport - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-transport-rxtx - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-transport-sctp - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-transport-udt - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-example - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-all - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-transport-native-unix-common - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-transport-native-unix-common - 4.1.34.1.dse + 4.1.34.2.dse linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.34.1.dse + 4.1.34.2.dse osx-x86_64 io.netty netty-transport-native-epoll - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-transport-native-epoll - 4.1.34.1.dse + 4.1.34.2.dse linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.34.1.dse + 4.1.34.2.dse io.netty netty-transport-native-kqueue - 4.1.34.1.dse + 4.1.34.2.dse osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 5ab107f734e4..74029b434c63 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 60e58834602f..06dad59b77da 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 1372db45e4a6..a51ac224d9c5 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 60d9ab8a9d8b..b61c319b5a43 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index 3cecd4af64ab..a97427f6ce82 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 286aed58df82..4fdb3017aa05 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 85326434b30c..4d749ef79532 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index cad555df74df..7988ca93bef9 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index 0b7db8cde8a4..1666e0364135 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 0fab8a1600c9..f3c39368030c 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index a6064c0bf2d2..8a2388037eec 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index 9dc14e327340..9ea5f8538362 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index f90e27842430..6b455474fd45 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-codec diff --git a/common/pom.xml b/common/pom.xml index 467b83d49334..91d62d522ae8 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index c839f7b2fa56..523999d283c7 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.34.1.dse + 4.1.34.2.dse Netty/Dev-Tools @@ -52,6 +52,6 @@ - netty-4.1.34.1.dse + netty-4.1.34.2.dse diff --git a/example/pom.xml b/example/pom.xml index 6b118acedc4e..51e1e9e36d85 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index e99350ab3221..d31ef689abd8 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index 51afe7ed3a70..f7a6535e8596 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 1e81064f04fd..41905a70dc8b 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-microbench diff --git a/pom.xml b/pom.xml index 790ffab1eb13..04eb02ab2d88 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.34.1.dse + 4.1.34.2.dse Netty http://netty.io/ @@ -53,7 +53,7 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.34.1.dse + netty-4.1.34.2.dse diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index 1ced2d2672ab..e8a9622d5db9 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index efece1580219..e0c7b05985df 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 09af27930dab..8165b9784ee3 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 34601d3edea1..5283ab2a5392 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index 7cd651a6cfab..d503940bf57d 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 67435598c177..8ac8d5f2fa27 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index 2c71bd40dd37..e87b4afad444 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index a88c56f68546..fb00796e4d60 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 2e3bbbcc9314..3f202b05a9ba 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index db9a10077d3b..d25b44954a0c 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index 0ea1cea25196..ca6f864e59b6 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 63d4b2f4adf6..4797cc7d20ca 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index 3a6b9adeb480..00541684c984 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index d3e7c5a65347..73d942baabad 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index f5e81f43e7c1..84ac430e6660 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index 6b9da8cf07b3..c3c4a8516191 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.1.dse + 4.1.34.2.dse netty-transport From 1731329e3bc1806993bf6fc642372155e23a10ca Mon Sep 17 00:00:00 2001 From: Jaroslaw Date: Tue, 10 Dec 2019 09:08:47 +0100 Subject: [PATCH 412/417] DSP-19407 after upgrade review remarks - Reason for reverting incrementMemoryCounter implementation (from Sergio's comment): "..moving from a CAS loop to the double addAndGet() might be faster, but by doing so, a single big-ish allocation which goes over the limit could cause concurrent small allocations to fail as well before the memory limit is brought back below threshold via DIRECT_MEMORY_COUNTER.addAndGet(-capacity)" - scheduledTaskQueue is now protected to simplify DB-3884 "we might need to peek at the scheduled queue ourselves" --- .../concurrent/AbstractScheduledEventExecutor.java | 2 +- .../io/netty/util/internal/PlatformDependent.java | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java index b2421609492e..f563ddcb8437 100644 --- a/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java +++ b/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java @@ -38,7 +38,7 @@ public int compare(ScheduledFutureTask o1, ScheduledFutureTask o2) { } }; - PriorityQueue> scheduledTaskQueue; + protected PriorityQueue> scheduledTaskQueue; protected AbstractScheduledEventExecutor() { } diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index b7d69868b1d7..a43a534424a3 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -673,13 +673,15 @@ public static void freeDirectNoCleaner(ByteBuffer buffer) { } private static void incrementMemoryCounter(int capacity) { - if (DIRECT_MEMORY_COUNTER != null) { - long newUsedMemory = DIRECT_MEMORY_COUNTER.addAndGet(capacity); + for (;;) { + long usedMemory = DIRECT_MEMORY_COUNTER.get(); + long newUsedMemory = usedMemory + capacity; if (DIRECT_MEMORY_LIMIT > 0 && newUsedMemory > DIRECT_MEMORY_LIMIT) { - DIRECT_MEMORY_COUNTER.addAndGet(-capacity); throw new OutOfDirectMemoryError("failed to allocate " + capacity - + " byte(s) of direct memory (used: " + (newUsedMemory - capacity) - + ", max: " + DIRECT_MEMORY_LIMIT + ')'); + + " byte(s) of direct memory (used: " + usedMemory + ", max: " + DIRECT_MEMORY_LIMIT + ')'); + } + if (DIRECT_MEMORY_COUNTER.compareAndSet(usedMemory, newUsedMemory)) { + break; } } } From 7eac8fb61c382a016f4581fc9975b46e41ea6360 Mon Sep 17 00:00:00 2001 From: Idel Pivnitskiy Date: Thu, 22 Aug 2019 04:58:22 -0700 Subject: [PATCH 413/417] Use `AppendableCharSequence.charAtUnsafe(int)` in `HttpObjectDecoder` (#9492) Motivation: `HttpObjectDecoder` pre-checks that it doesn't request characters outside of the `AppendableCharSequence`'s length. `0` is always allowed because the minimal length of `AppendableCharSequence` is `1`. We can legally skip index check by using `AppendableCharSequence.charAtUnsafe(int)` in all existing cases in `HttpObjectDecoder`. Modifications: - Use `AppendableCharSequence.charAtUnsafe(int)` instead of `AppendableCharSequence.charAt(int)` in `HttpObjectDecoder`. Result: No unnecessary index checks in `HttpObjectDecoder`. (cherry picked from commit 85fcf4e58164806645b79239d0e6bf00287e26dc) --- .../io/netty/handler/codec/http/HttpObjectDecoder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index 6d1d596f781c..b6ce94b63e34 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -575,7 +575,7 @@ private State readHeaders(ByteBuf buffer) { } if (line.length() > 0) { do { - char firstChar = line.charAt(0); + char firstChar = line.charAtUnsafe(0); if (name != null && (firstChar == ' ' || firstChar == '\t')) { //please do not make one line from below code //as it breaks +XX:OptimizeStringConcat optimization @@ -643,7 +643,7 @@ private LastHttpContent readTrailingHeaders(ByteBuf buffer) { trailer = this.trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders); } while (line.length() > 0) { - char firstChar = line.charAt(0); + char firstChar = line.charAtUnsafe(0); if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) { List current = trailer.trailingHeaders().getAll(lastHeader); if (!current.isEmpty()) { @@ -727,14 +727,14 @@ private void splitHeader(AppendableCharSequence sb) { nameStart = findNonWhitespace(sb, 0); for (nameEnd = nameStart; nameEnd < length; nameEnd ++) { - char ch = sb.charAt(nameEnd); + char ch = sb.charAtUnsafe(nameEnd); if (ch == ':' || Character.isWhitespace(ch)) { break; } } for (colonEnd = nameEnd; colonEnd < length; colonEnd ++) { - if (sb.charAt(colonEnd) == ':') { + if (sb.charAtUnsafe(colonEnd) == ':') { colonEnd ++; break; } From 55b7c072486afcf551dd75c677030969c31d4059 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 20 Sep 2019 21:02:11 +0200 Subject: [PATCH 414/417] Correctly handle whitespaces in HTTP header names as defined by RFC7230#section-3.2.4 (#9585) Motivation: When parsing HTTP headers special care needs to be taken when a whitespace is detected in the header name. Modifications: - Ignore whitespace when decoding response (just like before) - Throw exception when whitespace is detected during parsing - Add unit tests Result: Fixes https://github.com/netty/netty/issues/9571 (cherry picked from commit 39cafcb05c99f2aa9fce7e6597664c9ed6a63a95) --- .../handler/codec/http/HttpObjectDecoder.java | 16 +++++++++++++++- .../codec/http/HttpRequestDecoderTest.java | 14 ++++++++++++++ .../codec/http/HttpResponseDecoderTest.java | 15 +++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index b6ce94b63e34..818273c8d23e 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -728,7 +728,21 @@ private void splitHeader(AppendableCharSequence sb) { nameStart = findNonWhitespace(sb, 0); for (nameEnd = nameStart; nameEnd < length; nameEnd ++) { char ch = sb.charAtUnsafe(nameEnd); - if (ch == ':' || Character.isWhitespace(ch)) { + // https://tools.ietf.org/html/rfc7230#section-3.2.4 + // + // No whitespace is allowed between the header field-name and colon. In + // the past, differences in the handling of such whitespace have led to + // security vulnerabilities in request routing and response handling. A + // server MUST reject any received request message that contains + // whitespace between a header field-name and colon with a response code + // of 400 (Bad Request). A proxy MUST remove any such whitespace from a + // response message before forwarding the message downstream. + if (ch == ':' || + // In case of decoding a request we will just continue processing and header validation + // is done in the DefaultHttpHeaders implementation. + // + // In the case of decoding a response we will "skip" the whitespace. + (!isDecodingRequest() && Character.isWhitespace(ch))) { break; } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java index 45720631c40a..2b2d0cc5eb71 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java @@ -320,4 +320,18 @@ public void testTooLargeHeaders() { assertTrue(request.decoderResult().cause() instanceof TooLongFrameException); assertFalse(channel.finish()); } + + @Test + public void testWhitespace() { + EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); + String requestStr = "GET /some/path HTTP/1.1\r\n" + + "Transfer-Encoding : chunked\r\n" + + "Host: netty.io\n\r\n"; + + assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII))); + HttpRequest request = channel.readInbound(); + assertTrue(request.decoderResult().isFailure()); + assertTrue(request.decoderResult().cause() instanceof IllegalArgumentException); + assertFalse(channel.finish()); + } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java index f062da2bf1f4..6378a376b11c 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java @@ -712,4 +712,19 @@ public void testTrailerWithEmptyLineInSeparateBuffer() { assertFalse(channel.finish()); } + + @Test + public void testWhitespace() { + EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseDecoder()); + String requestStr = "HTTP/1.1 200 OK\r\n" + + "Transfer-Encoding : chunked\r\n" + + "Host: netty.io\n\r\n"; + + assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII))); + HttpResponse response = channel.readInbound(); + assertFalse(response.decoderResult().isFailure()); + assertEquals(HttpHeaderValues.CHUNKED.toString(), response.headers().get(HttpHeaderNames.TRANSFER_ENCODING)); + assertEquals("netty.io", response.headers().get(HttpHeaderNames.HOST)); + assertFalse(channel.finish()); + } } From 18ce1ac4e81583f8ed780eaffd4f81ecd13b344e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 11 Dec 2019 15:49:07 +0100 Subject: [PATCH 415/417] Detect missing colon when parsing http headers with no value (#9871) Motivation: Technical speaking its valid to have http headers with no values so we should support it. That said we need to detect if these are "generated" because of an "invalid" fold. Modifications: - Detect if a colon is missing when parsing headers. - Add unit test Result: Fixes https://github.com/netty/netty/issues/9866 (cherry picked from commit a7c18d44b46e02dadfe3da225a06e5091f5f328e) --- .../handler/codec/http/HttpObjectDecoder.java | 5 +++++ .../codec/http/HttpRequestDecoderTest.java | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index 818273c8d23e..171815266812 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -747,6 +747,11 @@ private void splitHeader(AppendableCharSequence sb) { } } + if (nameEnd == length) { + // There was no colon present at all. + throw new IllegalArgumentException("No colon found"); + } + for (colonEnd = nameEnd; colonEnd < length; colonEnd ++) { if (sb.charAtUnsafe(colonEnd) == ':') { colonEnd ++; diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java index 2b2d0cc5eb71..414a0336b524 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java @@ -334,4 +334,20 @@ public void testWhitespace() { assertTrue(request.decoderResult().cause() instanceof IllegalArgumentException); assertFalse(channel.finish()); } + + @Test + public void testHeaderWithNoValueAndMissingColon() { + EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); + String requestStr = "GET /some/path HTTP/1.1\r\n" + + "Content-Length: 0\r\n" + + "Host:\r\n" + + "netty.io\r\n\r\n"; + + assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII))); + HttpRequest request = channel.readInbound(); + System.err.println(request.headers().names().toString()); + assertTrue(request.decoderResult().isFailure()); + assertTrue(request.decoderResult().cause() instanceof IllegalArgumentException); + assertFalse(channel.finish()); + } } From db07502148322c068968452f54f9e7cb5b87d51b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 13 Dec 2019 08:53:19 +0100 Subject: [PATCH 416/417] Verify we do not receive multiple content-length headers or a content-length and transfer-encoding: chunked header when using HTTP/1.1 (#9865) Motivation: RFC7230 states that we should not accept multiple content-length headers and also should not accept a content-length header in combination with transfer-encoding: chunked Modifications: - Check for multiple content-length headers and if found mark message as invalid - Check if we found a content-length header and also a transfer-encoding: chunked and if so mark the message as invalid - Add unit test Result: Fixes https://github.com/netty/netty/issues/9861 (cherry picked from commit 8494b046ec7e4f28dbd44bc699cc4c4c92251729) --- .../handler/codec/http/HttpObjectDecoder.java | 50 +++++++++++++-- .../codec/http/HttpRequestDecoderTest.java | 64 ++++++++++++++++--- 2 files changed, 99 insertions(+), 15 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index 171815266812..8d4fc1d42957 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -600,23 +600,61 @@ private State readHeaders(ByteBuf buffer) { if (name != null) { headers.add(name, value); } + // reset name and value fields name = null; value = null; - State nextState; + List values = headers.getAll(HttpHeaderNames.CONTENT_LENGTH); + int contentLengthValuesCount = values.size(); + + if (contentLengthValuesCount > 0) { + // Guard against multiple Content-Length headers as stated in + // https://tools.ietf.org/html/rfc7230#section-3.3.2: + // + // If a message is received that has multiple Content-Length header + // fields with field-values consisting of the same decimal value, or a + // single Content-Length header field with a field value containing a + // list of identical decimal values (e.g., "Content-Length: 42, 42"), + // indicating that duplicate Content-Length header fields have been + // generated or combined by an upstream message processor, then the + // recipient MUST either reject the message as invalid or replace the + // duplicated field-values with a single valid Content-Length field + // containing that decimal value prior to determining the message body + // length or forwarding the message. + if (contentLengthValuesCount > 1 && message.protocolVersion() == HttpVersion.HTTP_1_1) { + throw new IllegalArgumentException("Multiple Content-Length headers found"); + } + contentLength = Long.parseLong(values.get(0)); + } if (isContentAlwaysEmpty(message)) { HttpUtil.setTransferEncodingChunked(message, false); - nextState = State.SKIP_CONTROL_CHARS; + return State.SKIP_CONTROL_CHARS; } else if (HttpUtil.isTransferEncodingChunked(message)) { - nextState = State.READ_CHUNK_SIZE; + // See https://tools.ietf.org/html/rfc7230#section-3.3.3 + // + // If a message is received with both a Transfer-Encoding and a + // Content-Length header field, the Transfer-Encoding overrides the + // Content-Length. Such a message might indicate an attempt to + // perform request smuggling (Section 9.5) or response splitting + // (Section 9.4) and ought to be handled as an error. A sender MUST + // remove the received Content-Length field prior to forwarding such + // a message downstream. + // + // This is also what http_parser does: + // https://github.com/nodejs/http-parser/blob/v2.9.2/http_parser.c#L1769 + if (contentLengthValuesCount > 0 && message.protocolVersion() == HttpVersion.HTTP_1_1) { + throw new IllegalArgumentException( + "Both 'Content-Length: " + contentLength + "' and 'Transfer-Encoding: chunked' found"); + } + + return State.READ_CHUNK_SIZE; } else if (contentLength() >= 0) { - nextState = State.READ_FIXED_LENGTH_CONTENT; + return State.READ_FIXED_LENGTH_CONTENT; } else { - nextState = State.READ_VARIABLE_LENGTH_CONTENT; + return State.READ_VARIABLE_LENGTH_CONTENT; } - return nextState; } private long contentLength() { diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java index 414a0336b524..717b58090158 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java @@ -323,29 +323,75 @@ public void testTooLargeHeaders() { @Test public void testWhitespace() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); String requestStr = "GET /some/path HTTP/1.1\r\n" + "Transfer-Encoding : chunked\r\n" + "Host: netty.io\n\r\n"; - - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII))); - HttpRequest request = channel.readInbound(); - assertTrue(request.decoderResult().isFailure()); - assertTrue(request.decoderResult().cause() instanceof IllegalArgumentException); - assertFalse(channel.finish()); + testInvalidHeaders0(requestStr); } @Test public void testHeaderWithNoValueAndMissingColon() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); String requestStr = "GET /some/path HTTP/1.1\r\n" + "Content-Length: 0\r\n" + "Host:\r\n" + "netty.io\r\n\r\n"; + testInvalidHeaders0(requestStr); + } + + @Test + public void testMultipleContentLengthHeaders() { + String requestStr = "GET /some/path HTTP/1.1\r\n" + + "Content-Length: 1\r\n" + + "Content-Length: 0\r\n\r\n" + + "b"; + testInvalidHeaders0(requestStr); + } + + @Test + public void testMultipleContentLengthHeaders2() { + String requestStr = "GET /some/path HTTP/1.1\r\n" + + "Content-Length: 1\r\n" + + "Connection: close\r\n" + + "Content-Length: 0\r\n\r\n" + + "b"; + testInvalidHeaders0(requestStr); + } + + @Test + public void testContentLengthHeaderWithCommaValue() { + String requestStr = "GET /some/path HTTP/1.1\r\n" + + "Content-Length: 1,1\r\n\r\n" + + "b"; + testInvalidHeaders0(requestStr); + } + @Test + public void testMultipleContentLengthHeadersWithFolding() { + String requestStr = "POST / HTTP/1.1\r\n" + + "Host: example.com\r\n" + + "Connection: close\r\n" + + "Content-Length: 5\r\n" + + "Content-Length:\r\n" + + "\t6\r\n\r\n" + + "123456"; + testInvalidHeaders0(requestStr); + } + + @Test + public void testContentLengthHeaderAndChunked() { + String requestStr = "POST / HTTP/1.1\r\n" + + "Host: example.com\r\n" + + "Connection: close\r\n" + + "Content-Length: 5\r\n" + + "Transfer-Encoding: chunked\r\n\r\n" + + "0\r\n\r\n"; + testInvalidHeaders0(requestStr); + } + + private static void testInvalidHeaders0(String requestStr) { + EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII))); HttpRequest request = channel.readInbound(); - System.err.println(request.headers().names().toString()); assertTrue(request.decoderResult().isFailure()); assertTrue(request.decoderResult().cause() instanceof IllegalArgumentException); assertFalse(channel.finish()); From 55207c3282422ac1ef418c47f869957eab02cf5f Mon Sep 17 00:00:00 2001 From: Dan LaRocque Date: Tue, 7 Apr 2020 06:11:25 -0500 Subject: [PATCH 417/417] Version bump to 4.1.34.3.dse Compared against 4.1.34.2.dse, this tag cherry-picks upstream commits that fixed bugs in HttpObjectDecoder/HttpRequestDecoder, plus two intermediate refactoring commits that indirectly affect those bugfix commits. What follows is a list of PR links, issue links, CVE links, and hashes associated with the cherry-picked commits. Verify we do not receive multiple content-length headers or a content-length and transfer-encoding: chunked header when using HTTP/1.1 (#9865) https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-7238 https://github.com/netty/netty/issues/9861 https://github.com/netty/netty/pull/9865 8494b046ec7e4f28dbd44bc699cc4c4c92251729 Detect missing colon when parsing http headers with no value (#9871) https://nvd.nist.gov/vuln/detail/CVE-2019-20444 https://github.com/netty/netty/issues/9866 https://github.com/netty/netty/pull/9871 a7c18d44b46e02dadfe3da225a06e5091f5f328e Fix typos in javadocs (#9527) skipped Correctly handle whitespaces in HTTP header names as defined by RFC7230#section-3.2.4 (#9585) https://nvd.nist.gov/vuln/detail/CVE-2019-16869 https://github.com/netty/netty/issues/9571 https://github.com/netty/netty/pull/9585 39cafcb05c99f2aa9fce7e6597664c9ed6a63a95 Use `AppendableCharSequence.charAtUnsafe(int)` in `HttpObjectDecoder` (#9492) https://github.com/netty/netty/pull/9492 85fcf4e58164806645b79239d0e6bf00287e26dc --- all/pom.xml | 2 +- bom/pom.xml | 67 +++++++++++----------- buffer/pom.xml | 2 +- codec-dns/pom.xml | 2 +- codec-haproxy/pom.xml | 2 +- codec-http/pom.xml | 2 +- codec-http2/pom.xml | 2 +- codec-memcache/pom.xml | 2 +- codec-mqtt/pom.xml | 2 +- codec-redis/pom.xml | 2 +- codec-smtp/pom.xml | 2 +- codec-socks/pom.xml | 2 +- codec-stomp/pom.xml | 2 +- codec-xml/pom.xml | 2 +- codec/pom.xml | 2 +- common/pom.xml | 2 +- dev-tools/pom.xml | 3 +- example/pom.xml | 2 +- handler-proxy/pom.xml | 2 +- handler/pom.xml | 2 +- microbench/pom.xml | 2 +- pom.xml | 3 +- resolver-dns/pom.xml | 2 +- resolver/pom.xml | 2 +- tarball/pom.xml | 2 +- testsuite-autobahn/pom.xml | 2 +- testsuite-http2/pom.xml | 2 +- testsuite-osgi/pom.xml | 2 +- testsuite-shading/pom.xml | 2 +- testsuite/pom.xml | 2 +- transport-native-epoll/pom.xml | 2 +- transport-native-kqueue/pom.xml | 2 +- transport-native-unix-common-tests/pom.xml | 2 +- transport-native-unix-common/pom.xml | 2 +- transport-rxtx/pom.xml | 2 +- transport-sctp/pom.xml | 2 +- transport-udt/pom.xml | 2 +- transport/pom.xml | 2 +- 38 files changed, 70 insertions(+), 73 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 53319253ff0c..e0ca51fc0bcb 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-all diff --git a/bom/pom.xml b/bom/pom.xml index 7a9fee8b3d3b..b78da2b70dc1 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -25,7 +25,7 @@ io.netty netty-bom - 4.1.34.2.dse + 4.1.34.3.dse pom Netty/BOM @@ -49,7 +49,6 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.34.2.dse @@ -69,165 +68,165 @@ io.netty netty-buffer - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec-dns - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec-haproxy - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec-http - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec-http2 - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec-memcache - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec-mqtt - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec-redis - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec-smtp - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec-socks - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec-stomp - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-codec-xml - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-common - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-dev-tools - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-handler - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-handler-proxy - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-resolver - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-resolver-dns - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-transport - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-transport-rxtx - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-transport-sctp - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-transport-udt - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-example - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-all - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-transport-native-unix-common - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-transport-native-unix-common - 4.1.34.2.dse + 4.1.34.3.dse linux-x86_64 io.netty netty-transport-native-unix-common - 4.1.34.2.dse + 4.1.34.3.dse osx-x86_64 io.netty netty-transport-native-epoll - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-transport-native-epoll - 4.1.34.2.dse + 4.1.34.3.dse linux-x86_64 io.netty netty-transport-native-kqueue - 4.1.34.2.dse + 4.1.34.3.dse io.netty netty-transport-native-kqueue - 4.1.34.2.dse + 4.1.34.3.dse osx-x86_64 diff --git a/buffer/pom.xml b/buffer/pom.xml index 74029b434c63..4e5586497c95 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-buffer diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml index 06dad59b77da..94de3b97e695 100644 --- a/codec-dns/pom.xml +++ b/codec-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec-dns diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index a51ac224d9c5..5f2ee5c3615f 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec-haproxy diff --git a/codec-http/pom.xml b/codec-http/pom.xml index b61c319b5a43..d3953462213f 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec-http diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml index a97427f6ce82..7973628d221b 100644 --- a/codec-http2/pom.xml +++ b/codec-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec-http2 diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml index 4fdb3017aa05..f54cfca998f2 100644 --- a/codec-memcache/pom.xml +++ b/codec-memcache/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec-memcache diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml index 4d749ef79532..08c050239111 100644 --- a/codec-mqtt/pom.xml +++ b/codec-mqtt/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec-mqtt diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml index 7988ca93bef9..b92ca2f0c714 100644 --- a/codec-redis/pom.xml +++ b/codec-redis/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec-redis diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml index 1666e0364135..d8570d8b204f 100644 --- a/codec-smtp/pom.xml +++ b/codec-smtp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec-smtp diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index f3c39368030c..67c68c3c4dc6 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec-socks diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml index 8a2388037eec..58ac05cfb581 100644 --- a/codec-stomp/pom.xml +++ b/codec-stomp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec-stomp diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml index 9ea5f8538362..ff0e5598a927 100644 --- a/codec-xml/pom.xml +++ b/codec-xml/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec-xml diff --git a/codec/pom.xml b/codec/pom.xml index 6b455474fd45..b36a60b8196a 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-codec diff --git a/common/pom.xml b/common/pom.xml index 91d62d522ae8..6c041034b3c9 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-common diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 523999d283c7..c3fb1e662da0 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -25,7 +25,7 @@ io.netty netty-dev-tools - 4.1.34.2.dse + 4.1.34.3.dse Netty/Dev-Tools @@ -52,6 +52,5 @@ - netty-4.1.34.2.dse diff --git a/example/pom.xml b/example/pom.xml index 51e1e9e36d85..b528fd6019d7 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-example diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml index d31ef689abd8..24f20e6e7e67 100644 --- a/handler-proxy/pom.xml +++ b/handler-proxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-handler-proxy diff --git a/handler/pom.xml b/handler/pom.xml index f7a6535e8596..028cea90bcf1 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-handler diff --git a/microbench/pom.xml b/microbench/pom.xml index 41905a70dc8b..75e7356f717f 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-microbench diff --git a/pom.xml b/pom.xml index 04eb02ab2d88..77de42766bc0 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.1.34.2.dse + 4.1.34.3.dse Netty http://netty.io/ @@ -53,7 +53,6 @@ https://github.com/netty/netty scm:git:git://github.com/netty/netty.git scm:git:ssh://git@github.com/netty/netty.git - netty-4.1.34.2.dse diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml index e8a9622d5db9..8c48330700f6 100644 --- a/resolver-dns/pom.xml +++ b/resolver-dns/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-resolver-dns diff --git a/resolver/pom.xml b/resolver/pom.xml index e0c7b05985df..9918805ef9f4 100644 --- a/resolver/pom.xml +++ b/resolver/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-resolver diff --git a/tarball/pom.xml b/tarball/pom.xml index 8165b9784ee3..b3f8c9f80988 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-tarball diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml index 5283ab2a5392..65d776c766e9 100644 --- a/testsuite-autobahn/pom.xml +++ b/testsuite-autobahn/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-testsuite-autobahn diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml index d503940bf57d..d4809c7779b2 100644 --- a/testsuite-http2/pom.xml +++ b/testsuite-http2/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-testsuite-http2 diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 8ac8d5f2fa27..47c564c859aa 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-testsuite-osgi diff --git a/testsuite-shading/pom.xml b/testsuite-shading/pom.xml index e87b4afad444..e461c7499473 100644 --- a/testsuite-shading/pom.xml +++ b/testsuite-shading/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-testsuite-shading diff --git a/testsuite/pom.xml b/testsuite/pom.xml index fb00796e4d60..319cce6fbd64 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-testsuite diff --git a/transport-native-epoll/pom.xml b/transport-native-epoll/pom.xml index 3f202b05a9ba..32f2771168c3 100644 --- a/transport-native-epoll/pom.xml +++ b/transport-native-epoll/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-transport-native-epoll diff --git a/transport-native-kqueue/pom.xml b/transport-native-kqueue/pom.xml index d25b44954a0c..fbb9284aa6bd 100644 --- a/transport-native-kqueue/pom.xml +++ b/transport-native-kqueue/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-transport-native-kqueue diff --git a/transport-native-unix-common-tests/pom.xml b/transport-native-unix-common-tests/pom.xml index ca6f864e59b6..62dbb1eda592 100644 --- a/transport-native-unix-common-tests/pom.xml +++ b/transport-native-unix-common-tests/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-transport-native-unix-common-tests diff --git a/transport-native-unix-common/pom.xml b/transport-native-unix-common/pom.xml index 4797cc7d20ca..10b6d5bc6bd0 100644 --- a/transport-native-unix-common/pom.xml +++ b/transport-native-unix-common/pom.xml @@ -19,7 +19,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-transport-native-unix-common diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index 00541684c984..37d4317d96b5 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-transport-rxtx diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index 73d942baabad..90d1230497dc 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-transport-sctp diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 84ac430e6660..f01ae9c19f75 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-transport-udt diff --git a/transport/pom.xml b/transport/pom.xml index c3c4a8516191..ff87fbec631d 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.1.34.2.dse + 4.1.34.3.dse netty-transport