From 19acf1a4b329811d450cf9d8658bcda0bb6e0b92 Mon Sep 17 00:00:00 2001 From: sullis Date: Fri, 18 Feb 2022 21:49:06 -0800 Subject: [PATCH 01/12] refactor io_uring unit test --- .github/workflows/ci-prb.yml | 3 +- .github/workflows/ci-prq.yml | 3 +- servicetalk-http-netty/build.gradle | 2 ++ .../servicetalk/http/netty/IoUringTest.java | 28 ++++++++++++++++--- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-prb.yml b/.github/workflows/ci-prb.yml index dcb7d76a48..3e4a940600 100644 --- a/.github/workflows/ci-prb.yml +++ b/.github/workflows/ci-prb.yml @@ -35,7 +35,8 @@ jobs: env: CI: true JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 - run: ./gradlew --parallel clean test + run: | + sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean test" - name: Upload Test Results if: always() uses: actions/upload-artifact@v2 diff --git a/.github/workflows/ci-prq.yml b/.github/workflows/ci-prq.yml index 0c9d64e295..4c3cbcc632 100644 --- a/.github/workflows/ci-prq.yml +++ b/.github/workflows/ci-prq.yml @@ -24,7 +24,8 @@ jobs: - name: Build with Gradle env: CI: true - run: ./gradlew --parallel clean quality + run: | + sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean quality" - name: Upload CheckStyle Results if: always() uses: actions/upload-artifact@v2 diff --git a/servicetalk-http-netty/build.gradle b/servicetalk-http-netty/build.gradle index a1cb4b0d4f..5437101f55 100644 --- a/servicetalk-http-netty/build.gradle +++ b/servicetalk-http-netty/build.gradle @@ -57,6 +57,8 @@ dependencies { testImplementation "io.netty:netty-transport-native-unix-common" testImplementation "io.netty:netty-tcnative-boringssl-static" testImplementation "io.netty.incubator:netty-incubator-transport-native-io_uring:$nettyIoUringVersion" + testImplementation "io.netty.incubator:netty-incubator-transport-native-io_uring:$nettyIoUringVersion:linux-x86_64" + testImplementation "io.netty.incubator:netty-incubator-transport-native-io_uring:$nettyIoUringVersion:linux-aarch_64" testImplementation "org.junit.jupiter:junit-jupiter-api" testImplementation "org.junit.jupiter:junit-jupiter-params" testImplementation "org.hamcrest:hamcrest:$hamcrestVersion" diff --git a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java index 90b3a3a048..5a72a7bef5 100644 --- a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java +++ b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java @@ -16,32 +16,49 @@ package io.servicetalk.http.netty; import io.servicetalk.http.api.BlockingHttpClient; +import io.servicetalk.http.api.HttpRequest; import io.servicetalk.http.api.HttpResponse; import io.servicetalk.transport.api.ServerContext; import io.servicetalk.transport.netty.internal.EventLoopAwareNettyIoExecutor; import io.servicetalk.transport.netty.internal.IoUringUtils; import io.servicetalk.transport.netty.internal.NettyIoExecutors; +import io.netty.incubator.channel.uring.IOUring; import io.netty.incubator.channel.uring.IOUringEventLoopGroup; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; import static io.servicetalk.http.api.HttpResponseStatus.OK; +import static io.servicetalk.http.api.HttpSerializers.textSerializerUtf8; import static io.servicetalk.http.netty.TestServiceStreaming.SVC_ECHO; import static io.servicetalk.transport.netty.internal.AddressUtils.localAddress; import static io.servicetalk.transport.netty.internal.AddressUtils.serverHostAndPort; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.condition.OS.LINUX; +import static org.junit.jupiter.api.condition.OS.MAC; class IoUringTest { @Test - void test() throws Exception { + @EnabledOnOs(value = { MAC }) + void ioUringIsNotAvailableOnMacOs() { + assertFalse(IOUring.isAvailable()); + assertFalse(IoUringUtils.isAvailable()); + } + + @Test + @EnabledOnOs(value = { LINUX }) + void ioUringIsAvailableOnLinux() throws Exception { + IOUring.ensureAvailability(); EventLoopAwareNettyIoExecutor ioUringExecutor = null; try { IoUringUtils.tryIoUring(true); - assumeTrue(IoUringUtils.isAvailable()); + assertTrue(IoUringUtils.isAvailable()); ioUringExecutor = NettyIoExecutors.createIoExecutor(2, "io-uring"); assertThat(ioUringExecutor.eventLoopGroup(), is(instanceOf(IOUringEventLoopGroup.class))); @@ -52,13 +69,16 @@ void test() throws Exception { BlockingHttpClient client = HttpClients.forSingleAddress(serverHostAndPort(serverContext)) .ioExecutor(ioUringExecutor) .buildBlocking()) { - HttpResponse response = client.request(client.get(SVC_ECHO)); + HttpRequest request = client.post(SVC_ECHO).payloadBody("bonjour!", textSerializerUtf8()); + HttpResponse response = client.request(request); assertThat(response.status(), is(OK)); + assertThat(response.payloadBody().toString(UTF_8), is("bonjour!")); } } finally { IoUringUtils.tryIoUring(false); if (ioUringExecutor != null) { ioUringExecutor.closeAsync().toFuture().get(); + assertTrue(ioUringExecutor.eventLoopGroup().isShutdown()); } } } From 532495e1b798b14173b92b4288071e7c2763315e Mon Sep 17 00:00:00 2001 From: sullis Date: Sat, 19 Feb 2022 17:34:06 -0800 Subject: [PATCH 02/12] introduce matrix.gradle-command --- .github/workflows/ci-prb.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-prb.yml b/.github/workflows/ci-prb.yml index 3e4a940600..4d4649217f 100644 --- a/.github/workflows/ci-prb.yml +++ b/.github/workflows/ci-prb.yml @@ -9,7 +9,12 @@ jobs: strategy: matrix: java: [ 8, 11, 17 ] - os: [ ubuntu-latest ] + os: [ ubuntu-latest, macos-latest ] + include: + - os: ubuntu-latest + gradle-command: sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean test" + - os: macos-latest + gradle-command: ./gradlew --no-daemon --parallel clean test steps: - name: Checkout Code uses: actions/checkout@v2.4.0 @@ -35,8 +40,7 @@ jobs: env: CI: true JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 - run: | - sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean test" + run: ${{ matrix.gradle-command }} - name: Upload Test Results if: always() uses: actions/upload-artifact@v2 From 0704b0e0526e339bb5b904f72b3d09ba20742e4c Mon Sep 17 00:00:00 2001 From: sullis Date: Sat, 19 Feb 2022 17:37:42 -0800 Subject: [PATCH 03/12] use Bash shell in ci-prb.yml --- .github/workflows/ci-prb.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-prb.yml b/.github/workflows/ci-prb.yml index 4d4649217f..9595ea099e 100644 --- a/.github/workflows/ci-prb.yml +++ b/.github/workflows/ci-prb.yml @@ -9,12 +9,7 @@ jobs: strategy: matrix: java: [ 8, 11, 17 ] - os: [ ubuntu-latest, macos-latest ] - include: - - os: ubuntu-latest - gradle-command: sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean test" - - os: macos-latest - gradle-command: ./gradlew --no-daemon --parallel clean test + os: [ ubuntu-latest ] steps: - name: Checkout Code uses: actions/checkout@v2.4.0 @@ -37,10 +32,16 @@ jobs: minimum-size: 8GB maximum-size: 16GB - name: Build and Test + shell: bash env: CI: true JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 - run: ${{ matrix.gradle-command }} + run: | + if [ "${{ matrix.os }}" = "Linux" ]; then + sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean test" + else + ./gradlew --no-daemon --parallel clean test + fi - name: Upload Test Results if: always() uses: actions/upload-artifact@v2 From ad17f8653e3c00a0a115bdd0fcd32fb011dc7952 Mon Sep 17 00:00:00 2001 From: sullis Date: Sat, 19 Feb 2022 18:04:42 -0800 Subject: [PATCH 04/12] Revert "use Bash shell in ci-prb.yml" This reverts commit b8cf1bbfbbfeaea7d8783c1d718839993f037d23. --- .github/workflows/ci-prb.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-prb.yml b/.github/workflows/ci-prb.yml index 9595ea099e..4d4649217f 100644 --- a/.github/workflows/ci-prb.yml +++ b/.github/workflows/ci-prb.yml @@ -9,7 +9,12 @@ jobs: strategy: matrix: java: [ 8, 11, 17 ] - os: [ ubuntu-latest ] + os: [ ubuntu-latest, macos-latest ] + include: + - os: ubuntu-latest + gradle-command: sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean test" + - os: macos-latest + gradle-command: ./gradlew --no-daemon --parallel clean test steps: - name: Checkout Code uses: actions/checkout@v2.4.0 @@ -32,16 +37,10 @@ jobs: minimum-size: 8GB maximum-size: 16GB - name: Build and Test - shell: bash env: CI: true JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 - run: | - if [ "${{ matrix.os }}" = "Linux" ]; then - sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean test" - else - ./gradlew --no-daemon --parallel clean test - fi + run: ${{ matrix.gradle-command }} - name: Upload Test Results if: always() uses: actions/upload-artifact@v2 From 22ea5fc16a2a7d478bfbb6d7b57dfa1835a01140 Mon Sep 17 00:00:00 2001 From: sullis Date: Sat, 19 Feb 2022 18:06:48 -0800 Subject: [PATCH 05/12] fix matrix in ci-prb.yml --- .github/workflows/ci-prb.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci-prb.yml b/.github/workflows/ci-prb.yml index 4d4649217f..8100ec6189 100644 --- a/.github/workflows/ci-prb.yml +++ b/.github/workflows/ci-prb.yml @@ -9,12 +9,10 @@ jobs: strategy: matrix: java: [ 8, 11, 17 ] - os: [ ubuntu-latest, macos-latest ] + os: [ ubuntu-latest ] include: - os: ubuntu-latest gradle-command: sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean test" - - os: macos-latest - gradle-command: ./gradlew --no-daemon --parallel clean test steps: - name: Checkout Code uses: actions/checkout@v2.4.0 From bb1ec0dc5e71d6eac85285f7c9c4e1adb2604a6d Mon Sep 17 00:00:00 2001 From: sullis Date: Sun, 20 Feb 2022 11:55:30 -0800 Subject: [PATCH 06/12] set matrix gradle-command in ci-prq.yml --- .github/workflows/ci-prq.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-prq.yml b/.github/workflows/ci-prq.yml index 4c3cbcc632..d1fa42bc3c 100644 --- a/.github/workflows/ci-prq.yml +++ b/.github/workflows/ci-prq.yml @@ -4,10 +4,15 @@ on: branches: [ main, '0.41', '0.42' ] jobs: quality: - runs-on: ubuntu-latest + name: quality ${{ matrix.java }} ${{ matrix.os }} + runs-on: ${{ matrix.os }} strategy: matrix: java: [ 8, 11, 17 ] + os: [ ubuntu-latest ] + include: + - os: ubuntu-latest + gradle-command: sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean quality" steps: - name: Checkout Code uses: actions/checkout@v2.4.0 @@ -24,8 +29,7 @@ jobs: - name: Build with Gradle env: CI: true - run: | - sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean quality" + run: ${{ matrix.gradle-command }} - name: Upload CheckStyle Results if: always() uses: actions/upload-artifact@v2 From 827ed003c41ff53f55e5d3ddd307085371c8a158 Mon Sep 17 00:00:00 2001 From: sullis Date: Sun, 20 Feb 2022 15:36:38 -0800 Subject: [PATCH 07/12] modify ci-release.yml and ci-snapshot.yml --- .github/workflows/ci-release.yml | 2 +- .github/workflows/ci-snapshot.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 4256ab64b5..032329b59d 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -56,7 +56,7 @@ jobs: fi # Execute the gradlew command to publish the build - ./gradlew --parallel -PreleaseBuild=true$FIRST_GRADLE_TARGETS && ./gradlew -PreleaseBuild=true$SECOND_GRADLE_TARGETS + sudo env "PATH=$PATH" "FIRST_GRADLE_TARGETS=$FIRST_GRADLE_TARGETS" "SECOND_GRADLE_TARGETS=$SECOND_GRADLE_TARGETS" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel -PreleaseBuild=true$FIRST_GRADLE_TARGETS && ./gradlew --no-daemon -PreleaseBuild=true$SECOND_GRADLE_TARGETS" - name: Publish Test Results if: always() uses: scacap/action-surefire-report@482f012643ed0560e23ef605a79e8e87ca081648 diff --git a/.github/workflows/ci-snapshot.yml b/.github/workflows/ci-snapshot.yml index 42c7ada031..91905d9e5b 100644 --- a/.github/workflows/ci-snapshot.yml +++ b/.github/workflows/ci-snapshot.yml @@ -57,7 +57,7 @@ jobs: fi # Execute the gradlew command to publish the build - ./gradlew --parallel$FIRST_GRADLE_TARGETS && ./gradlew$SECOND_GRADLE_TARGETS + sudo env "PATH=$PATH" "FIRST_GRADLE_TARGETS=$FIRST_GRADLE_TARGETS" "SECOND_GRADLE_TARGETS=$SECOND_GRADLE_TARGETS" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel$FIRST_GRADLE_TARGETS && ./gradlew --no-daemon$SECOND_GRADLE_TARGETS" - name: Publish Test Results if: always() uses: scacap/action-surefire-report@482f012643ed0560e23ef605a79e8e87ca081648 From 05affa6a6afdb8a3cf5d11b9f825861bf318877b Mon Sep 17 00:00:00 2001 From: sullis Date: Mon, 21 Feb 2022 18:42:30 -0800 Subject: [PATCH 08/12] restore ci-prq.yml --- .github/workflows/ci-prq.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-prq.yml b/.github/workflows/ci-prq.yml index d1fa42bc3c..0c9d64e295 100644 --- a/.github/workflows/ci-prq.yml +++ b/.github/workflows/ci-prq.yml @@ -4,15 +4,10 @@ on: branches: [ main, '0.41', '0.42' ] jobs: quality: - name: quality ${{ matrix.java }} ${{ matrix.os }} - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest strategy: matrix: java: [ 8, 11, 17 ] - os: [ ubuntu-latest ] - include: - - os: ubuntu-latest - gradle-command: sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean quality" steps: - name: Checkout Code uses: actions/checkout@v2.4.0 @@ -29,7 +24,7 @@ jobs: - name: Build with Gradle env: CI: true - run: ${{ matrix.gradle-command }} + run: ./gradlew --parallel clean quality - name: Upload CheckStyle Results if: always() uses: actions/upload-artifact@v2 From 8e5ec293e2272dbc933687d81ac631b20b546a1c Mon Sep 17 00:00:00 2001 From: sullis Date: Mon, 21 Feb 2022 18:49:02 -0800 Subject: [PATCH 09/12] linux specific build step in GitHub Actions --- .github/workflows/ci-prb.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-prb.yml b/.github/workflows/ci-prb.yml index 8100ec6189..b66f1edd3d 100644 --- a/.github/workflows/ci-prb.yml +++ b/.github/workflows/ci-prb.yml @@ -10,9 +10,6 @@ jobs: matrix: java: [ 8, 11, 17 ] os: [ ubuntu-latest ] - include: - - os: ubuntu-latest - gradle-command: sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean test" steps: - name: Checkout Code uses: actions/checkout@v2.4.0 @@ -34,11 +31,18 @@ jobs: with: minimum-size: 8GB maximum-size: 16GB - - name: Build and Test + - name: Build and Test (Linux) + if: runner.os == 'Linux' env: CI: true JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 - run: ${{ matrix.gradle-command }} + run: sudo env "PATH=$PATH" bash -c "ulimit -l 65536 && ulimit -a && ./gradlew --no-daemon --parallel clean test" + - name: Build and Test (non-Linux) + if: runner.os != 'Linux' + env: + CI: true + JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 + run: ./gradlew --no-daemon --parallel clean test - name: Upload Test Results if: always() uses: actions/upload-artifact@v2 From 53600a4a9bdc87ad896e40d216cbc94de962d8b2 Mon Sep 17 00:00:00 2001 From: sullis Date: Mon, 21 Feb 2022 19:05:04 -0800 Subject: [PATCH 10/12] improve assertions in ioUringIsNotAvailableOnMacOs --- .../test/java/io/servicetalk/http/netty/IoUringTest.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java index 5a72a7bef5..6e9b705f72 100644 --- a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java +++ b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java @@ -48,7 +48,14 @@ class IoUringTest { @EnabledOnOs(value = { MAC }) void ioUringIsNotAvailableOnMacOs() { assertFalse(IOUring.isAvailable()); - assertFalse(IoUringUtils.isAvailable()); + try { + IoUringUtils.tryIoUring(false); + assertFalse(IoUringUtils.isAvailable()); + IoUringUtils.tryIoUring(true); + assertFalse(IoUringUtils.isAvailable()); + } finally { + IoUringUtils.tryIoUring(false); + } } @Test From ec2c0bd7a0dcccf733fae2ad6cbcfe21478c53e6 Mon Sep 17 00:00:00 2001 From: sullis Date: Mon, 21 Feb 2022 19:07:20 -0800 Subject: [PATCH 11/12] cleanup ioUringIsAvailableOnLinux --- .../src/test/java/io/servicetalk/http/netty/IoUringTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java index 6e9b705f72..3fd6aa655e 100644 --- a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java +++ b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java @@ -61,11 +61,11 @@ void ioUringIsNotAvailableOnMacOs() { @Test @EnabledOnOs(value = { LINUX }) void ioUringIsAvailableOnLinux() throws Exception { - IOUring.ensureAvailability(); EventLoopAwareNettyIoExecutor ioUringExecutor = null; try { IoUringUtils.tryIoUring(true); assertTrue(IoUringUtils.isAvailable()); + IOUring.ensureAvailability(); ioUringExecutor = NettyIoExecutors.createIoExecutor(2, "io-uring"); assertThat(ioUringExecutor.eventLoopGroup(), is(instanceOf(IOUringEventLoopGroup.class))); @@ -79,7 +79,7 @@ void ioUringIsAvailableOnLinux() throws Exception { HttpRequest request = client.post(SVC_ECHO).payloadBody("bonjour!", textSerializerUtf8()); HttpResponse response = client.request(request); assertThat(response.status(), is(OK)); - assertThat(response.payloadBody().toString(UTF_8), is("bonjour!")); + assertThat(response.payloadBody(textSerializerUtf8()), is("bonjour!")); } } finally { IoUringUtils.tryIoUring(false); From 13cb2b99215426e42c218df2b0ce98a93692d3af Mon Sep 17 00:00:00 2001 From: sullis Date: Mon, 21 Feb 2022 19:13:42 -0800 Subject: [PATCH 12/12] remove unused import --- .../src/test/java/io/servicetalk/http/netty/IoUringTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java index 3fd6aa655e..4863f37fa7 100644 --- a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java +++ b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/IoUringTest.java @@ -33,7 +33,6 @@ import static io.servicetalk.http.netty.TestServiceStreaming.SVC_ECHO; import static io.servicetalk.transport.netty.internal.AddressUtils.localAddress; import static io.servicetalk.transport.netty.internal.AddressUtils.serverHostAndPort; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is;